From b064105da9bbc7f3fb30aa4cf635156773e51718 Mon Sep 17 00:00:00 2001 From: Mark Benvenuto Date: Tue, 23 Jul 2024 16:53:12 -0400 Subject: [PATCH] Lower bounds for double check to 53 --- src/mc-range-encoding.c | 7 +- test/test-mc-range-encoding.c | 135 +++++----------------------------- 2 files changed, 22 insertions(+), 120 deletions(-) diff --git a/src/mc-range-encoding.c b/src/mc-range-encoding.c index 644498f05..b4d87bc59 100644 --- a/src/mc-range-encoding.c +++ b/src/mc-range-encoding.c @@ -267,7 +267,10 @@ bool mc_canUsePrecisionModeDouble(double min, return false; } - if (*maxBitsOut >= 64) { + // Integers between -2^53 and 2^53 can be exactly represented. Outside this range, doubles lose precision by a + // multiple of 2^(n-52) where n = #bits. We disallow users from using precision mode when the bounds exceed 2^53 to + // prevent the users from being surprised by how floating point math works. + if (*maxBitsOut >= 53) { return false; } @@ -339,7 +342,7 @@ bool mc_getTypeInfoDouble(mc_getTypeInfoDouble_args_t args, } CLIENT_ERR("The domain of double values specified by the min, max, and precision cannot be represented in " - "fewer than 64 bits. min: %g, max: %g, precision: %" PRIu32, + "fewer than 53 bits. min: %g, max: %g, precision: %" PRIu32, args.min.value, args.max.value, args.precision.value); diff --git a/test/test-mc-range-encoding.c b/test/test-mc-range-encoding.c index 6ec6dd851..7ac35e56c 100644 --- a/test/test-mc-range-encoding.c +++ b/test/test-mc-range-encoding.c @@ -414,8 +414,7 @@ static void _test_RangeTest_Encode_Double(_mongocrypt_tester_t *tester) { .max = OPT_DOUBLE_C(5), .min = OPT_DOUBLE_C(0), .precision = OPT_U32_C(16), - .expect = 31415926535890000, - .expectMax = OPT_U64_C(72057594037927935)}, + .expectError = "The domain of double values specified by the min"}, {.value = -5, .max = OPT_DOUBLE_C(-1), .min = OPT_DOUBLE_C(-10), @@ -464,77 +463,31 @@ static void _test_RangeTest_Encode_Double(_mongocrypt_tester_t *tester) { .max = OPT_DOUBLE_C(10E-30), .min = OPT_DOUBLE_C(1E-30), .precision = OPT_U32_C(35), - // Applying min/max/precision result in a domain needing >= 64 bits to represent. + // Applying min/max/precision result in a domain needing >= 53 bits to represent. // For range v2, expect an error. - .expectError = "Invalid upper bounds for double precision."}, + .expectError = "Invalid upper bounds for double precision"}, {.value = 1E-30, .max = OPT_DOUBLE_C(10E-30), .min = OPT_DOUBLE_C(1E-30), .precision = OPT_U32_C(35), - // Applying min/max/precision result in a domain needing >= 64 bits to represent. + // Applying min/max/precision result in a domain needing >= 53 bits to represent. // For range v2, expect an error. - .expectError = "Invalid upper bounds for double precision."}, + .expectError = "Invalid upper bounds for double precision"}, /* Test max and min integer bounds for doubles */ {.value = DOUBLE_MIN_SAFE_INT, .max = OPT_DOUBLE_C(DOUBLE_MAX_SAFE_INT), .min = OPT_DOUBLE_C(DOUBLE_MIN_SAFE_INT), .precision = OPT_U32_C(0), .expect = 0, - .expectMax = OPT_U64_C(36028797018963967)}, - {.value = DOUBLE_MIN_SAFE_INT + 1, - .max = OPT_DOUBLE_C(DOUBLE_MAX_SAFE_INT), - .min = OPT_DOUBLE_C(DOUBLE_MIN_SAFE_INT), - .precision = OPT_U32_C(0), - .expect = 1, - .expectMax = OPT_U64_C(36028797018963967)}, - {.value = 0, - .max = OPT_DOUBLE_C(DOUBLE_MAX_SAFE_INT), - .min = OPT_DOUBLE_C(DOUBLE_MIN_SAFE_INT), - .precision = OPT_U32_C(0), - .expect = 9007199254740992, - .expectMax = OPT_U64_C(36028797018963967)}, - {.value = 1, - .max = OPT_DOUBLE_C(DOUBLE_MAX_SAFE_INT), - .min = OPT_DOUBLE_C(DOUBLE_MIN_SAFE_INT), - .precision = OPT_U32_C(0), - .expect = 9007199254740993, - .expectMax = OPT_U64_C(36028797018963967)}, - {.value = 2, - .max = OPT_DOUBLE_C(DOUBLE_MAX_SAFE_INT), - .min = OPT_DOUBLE_C(DOUBLE_MIN_SAFE_INT), - .precision = OPT_U32_C(0), - .expect = 9007199254740994, - .expectMax = OPT_U64_C(36028797018963967)}, - {.value = 3, - .max = OPT_DOUBLE_C(DOUBLE_MAX_SAFE_INT), - .min = OPT_DOUBLE_C(DOUBLE_MIN_SAFE_INT), - .precision = OPT_U32_C(0), - .expect = 9007199254740995, - .expectMax = OPT_U64_C(36028797018963967)}, - {.value = 4, - .max = OPT_DOUBLE_C(DOUBLE_MAX_SAFE_INT), - .min = OPT_DOUBLE_C(DOUBLE_MIN_SAFE_INT), - .precision = OPT_U32_C(0), - .expect = 9007199254740996, - .expectMax = OPT_U64_C(36028797018963967)}, - {.value = 5, - .max = OPT_DOUBLE_C(DOUBLE_MAX_SAFE_INT), - .min = OPT_DOUBLE_C(DOUBLE_MIN_SAFE_INT), - .precision = OPT_U32_C(0), - .expect = 9007199254740997, - .expectMax = OPT_U64_C(36028797018963967)}, - {.value = 0, - .max = OPT_DOUBLE_C(DOUBLE_MAX_SAFE_INT), - .min = OPT_DOUBLE_C(DOUBLE_MIN_SAFE_INT), - .precision = OPT_U32_C(0), - .expect = 9007199254740992, - .expectMax = OPT_U64_C(36028797018963967)}, - {.value = DOUBLE_MAX_SAFE_INT, - .max = OPT_DOUBLE_C(DOUBLE_MAX_SAFE_INT), - .min = OPT_DOUBLE_C(DOUBLE_MIN_SAFE_INT), + // Applying min/max/precision result in a domain needing >= 53 bits to represent. + // For range v2, expect an error. + .expectError = "The domain of double values specified by the min"}, + {.value = 900719925474099.6, + .max = OPT_DOUBLE_C(900719925474100.0), + .min = OPT_DOUBLE_C(900719925474099.0), .precision = OPT_U32_C(0), - .expect = 18014398509481984, - .expectMax = OPT_U64_C(36028797018963967)}, + .expect = 0, + .expectMax = OPT_U64_C(1)}, // 2^52 // The expected values increase by 4503599627370496 * 2^(i-52) + j // i.e. the gaps between integers as the exponent increases since doubles lose precision as @@ -543,64 +496,10 @@ static void _test_RangeTest_Encode_Double(_mongocrypt_tester_t *tester) { .max = OPT_DOUBLE_C(4503599627370496), .min = OPT_DOUBLE_C(-4503599627370496), .precision = OPT_U32_C(0), - .expect = 4503599627370496, - .expectMax = OPT_U64_C(18014398509481983)}, - // 2^53 - {.value = 1, - .max = OPT_DOUBLE_C(9007199254740992), - .min = OPT_DOUBLE_C(-9007199254740992), - .precision = OPT_U32_C(0), - .expect = 9007199254740993, - .expectMax = OPT_U64_C(36028797018963967)}, - // 2^54 - {.value = 2, - .max = OPT_DOUBLE_C(18014398509481984), - .min = OPT_DOUBLE_C(-18014398509481984), - .precision = OPT_U32_C(0), - .expect = 18014398509481986, - .expectMax = OPT_U64_C(72057594037927935)}, - // 2^55 - {.value = 3, - .max = OPT_DOUBLE_C(36028797018963968), - .min = OPT_DOUBLE_C(-36028797018963968), - .precision = OPT_U32_C(0), - .expect = 36028797018963971, - .expectMax = OPT_U64_C(144115188075855871)}, - // 2^56 - {.value = 4, - .max = OPT_DOUBLE_C(72057594037927936), - .min = OPT_DOUBLE_C(-72057594037927936), - .precision = OPT_U32_C(0), - .expect = 72057594037927940, - .expectMax = OPT_U64_C(288230376151711743)}, - // 2^57 - {.value = 1, - .max = OPT_DOUBLE_C(144115188075855872), - .min = OPT_DOUBLE_C(-144115188075855872), - .precision = OPT_U32_C(0), - .expect = 144115188075855873, - .expectMax = OPT_U64_C(576460752303423487)}, - // 2^58 - {.value = 2, - .max = OPT_DOUBLE_C(288230376151711744), - .min = OPT_DOUBLE_C(-288230376151711744), - .precision = OPT_U32_C(0), - .expect = 288230376151711746, - .expectMax = OPT_U64_C(1152921504606846975)}, - // 2^59 - {.value = 3, - .max = OPT_DOUBLE_C(576460752303423488), - .min = OPT_DOUBLE_C(-576460752303423488), - .precision = OPT_U32_C(0), - .expect = 576460752303423491, - .expectMax = OPT_U64_C(2305843009213693951)}, - // 2^60 - {.value = 4, - .max = OPT_DOUBLE_C(1152921504606846976), - .min = OPT_DOUBLE_C(-1152921504606846976), - .precision = OPT_U32_C(0), - .expect = 1152921504606846980, - .expectMax = OPT_U64_C(4611686018427387903)}, + .expect = 0, + // Applying min/max/precision result in a domain needing >= 53 bits to represent. + // For range v2, expect an error. + .expectError = "The domain of double values specified by the min"}, /* Test cases copied from Double_Bounds_Precision ... end */ {.value = -1, .min = OPT_DOUBLE_C(0),