From e7790540c6e1cbc1a275d718df5492991e780784 Mon Sep 17 00:00:00 2001 From: Jesse Alama Date: Tue, 30 Apr 2024 12:28:15 +0200 Subject: [PATCH] Remove inofficial rounding modes These rounding modes are optional/unsupported in IEEE Decimal128 and thus may not be supported in compiler libraries or hardware. --- src/decimal128.mts | 195 +------------------------------------- tests/constructor.test.js | 8 -- tests/round.test.js | 109 --------------------- 3 files changed, 1 insertion(+), 311 deletions(-) diff --git a/src/decimal128.mts b/src/decimal128.mts index 903371e..4cf6d0e 100644 --- a/src/decimal128.mts +++ b/src/decimal128.mts @@ -393,41 +393,6 @@ function roundFloor(x: SignedSignificandExponent): SignedSignificandExponent { }; } -function roundExpand(x: SignedSignificandExponent): SignedSignificandExponent { - let sig = x.significand.toString(); - let lastDigit = parseInt(sig.charAt(MAX_SIGNIFICANT_DIGITS)) as Digit; - let cutoff = cutoffAfterSignificantDigits(sig, MAX_SIGNIFICANT_DIGITS - 1); - let penultimateDigit = parseInt( - sig.charAt(MAX_SIGNIFICANT_DIGITS - 1) - ) as Digit; - let excessDigits = sig.substring(MAX_SIGNIFICANT_DIGITS); - let numExcessDigits = excessDigits.length; - let exp = x.exponent + numExcessDigits; // we will chop off the excess digits - - let finalDigit = roundIt( - x.isNegative, - penultimateDigit, - lastDigit, - ROUNDING_MODE_EXPAND - ); - - if (finalDigit < 10) { - return { - isNegative: x.isNegative, - significand: BigInt(`${cutoff}${finalDigit}`), - exponent: exp, - }; - } - - let rounded = propagateCarryFromRight(cutoff); - - return { - isNegative: x.isNegative, - significand: BigInt(`${rounded}0`), - exponent: exp, - }; -} - function roundTrunc(x: SignedSignificandExponent): SignedSignificandExponent { let sig = x.significand.toString(); let lastDigit = parseInt(sig.charAt(MAX_SIGNIFICANT_DIGITS)) as Digit; @@ -453,43 +418,6 @@ function roundTrunc(x: SignedSignificandExponent): SignedSignificandExponent { }; } -function roundHalfExpand( - x: SignedSignificandExponent -): SignedSignificandExponent { - let sig = x.significand.toString(); - let lastDigit = parseInt(sig.charAt(MAX_SIGNIFICANT_DIGITS)) as Digit; - let cutoff = cutoffAfterSignificantDigits(sig, MAX_SIGNIFICANT_DIGITS - 1); - let penultimateDigit = parseInt( - sig.charAt(MAX_SIGNIFICANT_DIGITS - 1) - ) as Digit; - let excessDigits = sig.substring(MAX_SIGNIFICANT_DIGITS); - let numExcessDigits = excessDigits.length; - let exp = x.exponent + numExcessDigits; // we will chop off the excess digits - - let finalDigit = roundIt( - x.isNegative, - penultimateDigit, - lastDigit, - ROUNDING_MODE_HALF_EXPAND - ); - - if (finalDigit < 10) { - return { - isNegative: x.isNegative, - significand: BigInt(`${cutoff}${finalDigit}`), - exponent: exp, - }; - } - - let rounded = propagateCarryFromRight(cutoff); - - return { - isNegative: x.isNegative, - significand: BigInt(`${rounded}0`), - exponent: exp, - }; -} - function roundHalfCeil( x: SignedSignificandExponent ): SignedSignificandExponent { @@ -517,70 +445,6 @@ function roundHalfCeil( }; } -function roundHalfFloor( - x: SignedSignificandExponent -): SignedSignificandExponent { - let sig = x.significand.toString(); - let lastDigit = parseInt(sig.charAt(MAX_SIGNIFICANT_DIGITS)) as Digit; - let cutoff = cutoffAfterSignificantDigits(sig, MAX_SIGNIFICANT_DIGITS - 1); - let penultimateDigit = parseInt( - sig.charAt(MAX_SIGNIFICANT_DIGITS - 1) - ) as Digit; - let excessDigits = sig.substring(MAX_SIGNIFICANT_DIGITS); - let numExcessDigits = excessDigits.length; - let exp = x.exponent + numExcessDigits; // we will chop off the excess digits - - let finalDigit = roundIt( - x.isNegative, - penultimateDigit, - lastDigit, - ROUNDING_MODE_HALF_FLOOR - ); - - if (finalDigit < 10) { - return { - isNegative: x.isNegative, - significand: BigInt(`${cutoff}${finalDigit}`), - exponent: exp, - }; - } - - let rounded = propagateCarryFromRight(cutoff); - - return { - isNegative: x.isNegative, - significand: BigInt(`${rounded}0`), - exponent: exp, - }; -} - -function roundHalfTrunc( - x: SignedSignificandExponent -): SignedSignificandExponent { - let sig = x.significand.toString(); - let lastDigit = parseInt(sig.charAt(MAX_SIGNIFICANT_DIGITS)) as Digit; - let cutoff = cutoffAfterSignificantDigits(sig, MAX_SIGNIFICANT_DIGITS - 1); - let penultimateDigit = parseInt( - sig.charAt(MAX_SIGNIFICANT_DIGITS - 1) - ) as Digit; - let excessDigits = sig.substring(MAX_SIGNIFICANT_DIGITS); - let numExcessDigits = excessDigits.length; - let exp = x.exponent + numExcessDigits; // we will chop off the excess digits - - let finalDigit = roundIt( - x.isNegative, - penultimateDigit, - lastDigit, - ROUNDING_MODE_HALF_TRUNCATE - ); - - return { - isNegative: x.isNegative, - significand: BigInt(`${cutoff}${finalDigit}`), - exponent: exp, - }; -} - function adjustNonInteger( x: SignedSignificandExponent, options: FullySpecifiedConstructorOptions @@ -592,18 +456,10 @@ function adjustNonInteger( return roundCeiling(x); case ROUNDING_MODE_FLOOR: return roundFloor(x); - case ROUNDING_MODE_EXPAND: - return roundExpand(x); case ROUNDING_MODE_TRUNCATE: return roundTrunc(x); - case ROUNDING_MODE_HALF_EXPAND: - return roundHalfExpand(x); case ROUNDING_MODE_HALF_CEILING: return roundHalfCeil(x); - case ROUNDING_MODE_HALF_FLOOR: - return roundHalfFloor(x); - case ROUNDING_MODE_HALF_TRUNCATE: - return roundHalfTrunc(x); default: return roundHalfEven(x); } @@ -718,13 +574,9 @@ function handleInfinity(s: string): Decimal128Constructor { export const ROUNDING_MODE_CEILING: RoundingMode = "ceil"; export const ROUNDING_MODE_FLOOR: RoundingMode = "floor"; -export const ROUNDING_MODE_EXPAND: RoundingMode = "expand"; export const ROUNDING_MODE_TRUNCATE: RoundingMode = "trunc"; export const ROUNDING_MODE_HALF_EVEN: RoundingMode = "halfEven"; -export const ROUNDING_MODE_HALF_EXPAND: RoundingMode = "halfExpand"; export const ROUNDING_MODE_HALF_CEILING: RoundingMode = "halfCeil"; -export const ROUNDING_MODE_HALF_FLOOR: RoundingMode = "halfFloor"; -export const ROUNDING_MODE_HALF_TRUNCATE: RoundingMode = "halfTrunc"; const ROUNDING_MODE_DEFAULT = ROUNDING_MODE_HALF_EVEN; const CONSTRUCTOR_SHOULD_NORMALIZE = false; @@ -756,8 +608,6 @@ function roundIt( } return digitToRound; - case ROUNDING_MODE_EXPAND: - return (digitToRound + 1) as DigitOrTen; case ROUNDING_MODE_TRUNCATE: return digitToRound; case ROUNDING_MODE_HALF_CEILING: @@ -769,36 +619,6 @@ function roundIt( return (digitToRound + 1) as DigitOrTen; } - return digitToRound; - case ROUNDING_MODE_HALF_FLOOR: - if (decidingDigit === 5) { - if (isNegative) { - return (digitToRound + 1) as DigitOrTen; - } - - return digitToRound; - } - - if (decidingDigit > 5) { - return (digitToRound + 1) as DigitOrTen; - } - - return digitToRound; - case ROUNDING_MODE_HALF_TRUNCATE: - if (decidingDigit === 5) { - return digitToRound; - } - - if (decidingDigit > 5) { - return (digitToRound + 1) as DigitOrTen; - } - - return digitToRound; - case ROUNDING_MODE_HALF_EXPAND: - if (decidingDigit >= 5) { - return (digitToRound + 1) as DigitOrTen; - } - return digitToRound; default: // ROUNDING_MODE_HALF_EVEN: if (decidingDigit === 5) { @@ -817,27 +637,14 @@ function roundIt( } } -type RoundingMode = - | "ceil" - | "floor" - | "expand" - | "trunc" - | "halfEven" - | "halfExpand" - | "halfCeil" - | "halfFloor" - | "halfTrunc"; +type RoundingMode = "ceil" | "floor" | "trunc" | "halfEven" | "halfCeil"; const ROUNDING_MODES: RoundingMode[] = [ "ceil", "floor", - "expand", "trunc", "halfEven", - "halfExpand", "halfCeil", - "halfFloor", - "halfTrunc", ]; const digitStrRegExp = diff --git a/tests/constructor.test.js b/tests/constructor.test.js index 3c93037..49690af 100644 --- a/tests/constructor.test.js +++ b/tests/constructor.test.js @@ -464,13 +464,9 @@ describe("rounding options", () => { let answers = { ceil: "-1234567890123456789012345678901234", floor: "-1234567890123456789012345678901235", - expand: "-1234567890123456789012345678901235", trunc: "-1234567890123456789012345678901234", halfEven: "-1234567890123456789012345678901234", - halfExpand: "-1234567890123456789012345678901235", halfCeil: "-1234567890123456789012345678901234", - halfFloor: "-1234567890123456789012345678901235", - halfTrunc: "-1234567890123456789012345678901234", }; for (const [mode, expected] of Object.entries(answers)) { test(`constructor with rounding mode "${mode}"`, () => { @@ -485,13 +481,9 @@ describe("rounding options", () => { let roundUpAnswers = { ceil: "-1234567890123456789012345678901239", floor: "-1234567890123456789012345678901240", - expand: "-1234567890123456789012345678901240", trunc: "-1234567890123456789012345678901239", halfEven: "-1234567890123456789012345678901240", - halfExpand: "-1234567890123456789012345678901240", halfCeil: "-1234567890123456789012345678901239", - halfFloor: "-1234567890123456789012345678901240", - halfTrunc: "-1234567890123456789012345678901239", }; for (const [mode, expected] of Object.entries(roundUpAnswers)) { test(`constructor with rounding mode "${mode}"`, () => { diff --git a/tests/round.test.js b/tests/round.test.js index e50b408..e00cc84 100644 --- a/tests/round.test.js +++ b/tests/round.test.js @@ -131,33 +131,6 @@ describe("Intl.NumberFormat examples", () => { ); }); }); - describe("expand", () => { - test("-1.5", () => { - expect( - minusOnePointFive.round(0, "expand").toString() - ).toStrictEqual("-2"); - }); - test("0.4", () => { - expect(zeroPointFour.round(0, "expand").toString()).toStrictEqual( - "1" - ); - }); - test("0.5", () => { - expect(zeroPointFive.round(0, "expand").toString()).toStrictEqual( - "1" - ); - }); - test("0.6", () => { - expect(zeroPointSix.round(0, "expand").toString()).toStrictEqual( - "1" - ); - }); - test("1.5", () => { - expect(onePointFive.round(0, "expand").toString()).toStrictEqual( - "2" - ); - }); - }); describe("trunc", () => { test("-1.5", () => { expect( @@ -212,88 +185,6 @@ describe("Intl.NumberFormat examples", () => { ); }); }); - describe("halfFloor", () => { - test("-1.5", () => { - expect( - minusOnePointFive.round(0, "halfFloor").toString() - ).toStrictEqual("-2"); - }); - test("0.4", () => { - expect( - zeroPointFour.round(0, "halfFloor").toString() - ).toStrictEqual("0"); - }); - test("0.5", () => { - expect( - zeroPointFive.round(0, "halfFloor").toString() - ).toStrictEqual("0"); - }); - test("0.6", () => { - expect(zeroPointSix.round(0, "halfFloor").toString()).toStrictEqual( - "1" - ); - }); - test("1.5", () => { - expect(onePointFive.round(0, "halfFloor").toString()).toStrictEqual( - "1" - ); - }); - }); - describe("halfExpand", () => { - let opts = { "rounding-mode": "halfExpand" }; - test("-1.5", () => { - expect( - minusOnePointFive.round(0, "halfExpand").toString() - ).toStrictEqual("-2"); - }); - test("0.4", () => { - expect( - zeroPointFour.round(0, "halfExpand").toString() - ).toStrictEqual("0"); - }); - test("0.5", () => { - expect( - zeroPointFive.round(0, "halfExpand").toString() - ).toStrictEqual("1"); - }); - test("0.6", () => { - expect( - zeroPointSix.round(0, "halfExpand").toString() - ).toStrictEqual("1"); - }); - test("1.5", () => { - expect( - onePointFive.round(0, "halfExpand").toString() - ).toStrictEqual("2"); - }); - }); - describe("halfTrunc", () => { - test("-1.5", () => { - expect( - minusOnePointFive.round(0, "halfTrunc").toString() - ).toStrictEqual("-1"); - }); - test("0.4", () => { - expect( - zeroPointFour.round(0, "halfTrunc").toString() - ).toStrictEqual("0"); - }); - test("0.5", () => { - expect( - zeroPointFive.round(0, "halfTrunc").toString() - ).toStrictEqual("0"); - }); - test("0.6", () => { - expect(zeroPointSix.round(0, "halfTrunc").toString()).toStrictEqual( - "1" - ); - }); - test("1.5", () => { - expect(onePointFive.round(0, "halfTrunc").toString()).toStrictEqual( - "1" - ); - }); - }); describe("halfEven", () => { test("-1.5", () => { expect(