Browse Source

AK+LibJS: Make `Number.MIN_VALUE` a denormal

ECMA-262 implies that `MIN_VALUE` should be a denormalized value if
denormal arithmetic is supported. This is the case on x86-64 and AArch64
using standard GCC/Clang compilation settings.

test262 checks whether `Number.MIN_VALUE / 2.0` is equal to 0, which
only holds if `MIN_VALUE` is the smallest denormalized value.

This commit renames the existing `NumericLimits<FloatingPoint>::min()`
to `min_normal()` and adds a `min_denormal()` method to force users to
explicitly think about which one is appropriate for their use case. We
shouldn't follow the STL's confusingly designed interface in this
regard.
Daniel Bertalan 2 years ago
parent
commit
0cd85ab0fc

+ 6 - 3
AK/NumericLimits.h

@@ -114,7 +114,8 @@ struct NumericLimits<unsigned long long> {
 template<>
 struct NumericLimits<float> {
     static constexpr float lowest() { return -__FLT_MAX__; }
-    static constexpr float min() { return __FLT_MIN__; }
+    static constexpr float min_normal() { return __FLT_MIN__; }
+    static constexpr float min_denormal() { return __FLT_DENORM_MIN__; }
     static constexpr float max() { return __FLT_MAX__; }
     static constexpr float epsilon() { return __FLT_EPSILON__; }
     static constexpr bool is_signed() { return true; }
@@ -124,7 +125,8 @@ struct NumericLimits<float> {
 template<>
 struct NumericLimits<double> {
     static constexpr double lowest() { return -__DBL_MAX__; }
-    static constexpr double min() { return __DBL_MIN__; }
+    static constexpr double min_normal() { return __DBL_MIN__; }
+    static constexpr double min_denormal() { return __DBL_DENORM_MIN__; }
     static constexpr double max() { return __DBL_MAX__; }
     static constexpr double epsilon() { return __DBL_EPSILON__; }
     static constexpr bool is_signed() { return true; }
@@ -134,7 +136,8 @@ struct NumericLimits<double> {
 template<>
 struct NumericLimits<long double> {
     static constexpr long double lowest() { return -__LDBL_MAX__; }
-    static constexpr long double min() { return __LDBL_MIN__; }
+    static constexpr long double min_normal() { return __LDBL_MIN__; }
+    static constexpr long double min_denormal() { return __LDBL_DENORM_MIN__; }
     static constexpr long double max() { return __LDBL_MAX__; }
     static constexpr long double epsilon() { return __LDBL_EPSILON__; }
     static constexpr bool is_signed() { return true; }

+ 1 - 1
Userland/Libraries/LibJS/Runtime/NumberConstructor.cpp

@@ -46,7 +46,7 @@ ThrowCompletionOr<void> NumberConstructor::initialize(Realm& realm)
     define_direct_property(vm.names.parseFloat, realm.intrinsics().parse_float_function(), attr);
     define_direct_property(vm.names.EPSILON, Value(EPSILON_VALUE), 0);
     define_direct_property(vm.names.MAX_VALUE, Value(NumericLimits<double>::max()), 0);
-    define_direct_property(vm.names.MIN_VALUE, Value(NumericLimits<double>::min()), 0);
+    define_direct_property(vm.names.MIN_VALUE, Value(NumericLimits<double>::min_denormal()), 0);
     define_direct_property(vm.names.MAX_SAFE_INTEGER, Value(MAX_SAFE_INTEGER_VALUE), 0);
     define_direct_property(vm.names.MIN_SAFE_INTEGER, Value(MIN_SAFE_INTEGER_VALUE), 0);
     define_direct_property(vm.names.NEGATIVE_INFINITY, js_negative_infinity(), 0);

+ 2 - 0
Userland/Libraries/LibJS/Tests/builtins/Number/Number-constants.js

@@ -8,4 +8,6 @@ test("basic functionality", () => {
     expect(Number.POSITIVE_INFINITY).toBe(Infinity);
     expect(Number.NEGATIVE_INFINITY).toBe(-Infinity);
     expect(Number.NaN).toBeNaN();
+    expect(Number.MIN_VALUE).toBeGreaterThan(0);
+    expect(Number.MIN_VALUE / 2.0).toBe(0);
 });