Source code

Revision control

Copy as Markdown

Other Tools

// |reftest| shell-option(--enable-float16array) skip-if(!Math.f16round)
// Some tests regarding conversion to Float16
assertEq(Math.f16round(), NaN);
// Special values
assertEq(Math.f16round(NaN), NaN);
assertEq(Math.f16round(-Infinity), -Infinity);
assertEq(Math.f16round(Infinity), Infinity);
assertEq(Math.f16round(-0), -0);
assertEq(Math.f16round(+0), +0);
// Polyfill function for Float16 conversion, adapted from
// The original license is preseved below.
function toFloat16(num) {
// MIT License
// Copyright (c) 2017-2024 Kenta Moriuchi
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
const INVERSE_OF_EPSILON = 1 / Number.EPSILON;
/**
* rounds to the nearest value;
* if the number falls midway, it is rounded to the nearest value with an even least significant digit
* @param {number} num
* @returns {number}
*/
function roundTiesToEven(num) {
return (num + INVERSE_OF_EPSILON) - INVERSE_OF_EPSILON;
}
const FLOAT16_MIN_VALUE = 6.103515625e-05;
const FLOAT16_MAX_VALUE = 65504;
const FLOAT16_EPSILON = 0.0009765625;
const FLOAT16_EPSILON_MULTIPLIED_BY_FLOAT16_MIN_VALUE = FLOAT16_EPSILON * FLOAT16_MIN_VALUE;
const FLOAT16_EPSILON_DEVIDED_BY_EPSILON = FLOAT16_EPSILON * INVERSE_OF_EPSILON;
function roundToFloat16(num) {
const number = +num;
// NaN, Infinity, -Infinity, 0, -0
if (!isFinite(number) || number === 0) {
return number;
}
// finite except 0, -0
const sign = number > 0 ? 1 : -1;
const absolute = Math.abs(number);
// small number
if (absolute < FLOAT16_MIN_VALUE) {
return sign * roundTiesToEven(absolute / FLOAT16_EPSILON_MULTIPLIED_BY_FLOAT16_MIN_VALUE) * FLOAT16_EPSILON_MULTIPLIED_BY_FLOAT16_MIN_VALUE;
}
const temp = (1 + FLOAT16_EPSILON_DEVIDED_BY_EPSILON) * absolute;
const result = temp - (temp - absolute);
// large number
if (result > FLOAT16_MAX_VALUE || isNaN(result)) {
return sign * Infinity;
}
return sign * result;
}
return roundToFloat16(num);
};
// A test on a certain range of numbers, including big numbers, so that
// we get a loss in precision for some of them.
for (var i = 0; i < 64; ++i) {
var p = Math.pow(2, i) + 1;
assertEq(Math.f16round(p), toFloat16(p));
assertEq(Math.f16round(-p), toFloat16(-p));
}
/********************************************
/* Tests on maximal Float16 / Double values *
/*******************************************/
function maxValue(exponentWidth, significandWidth) {
var n = 0;
var maxExp = Math.pow(2, exponentWidth - 1) - 1;
for (var i = significandWidth; i >= 0; i--)
n += Math.pow(2, maxExp - i);
return n;
}
var DBL_MAX = maxValue(11, 52);
assertEq(DBL_MAX, Number.MAX_VALUE); // sanity check
// Finite as a double, too big for a float16
assertEq(Math.f16round(DBL_MAX), Infinity);
var FLT16_MAX = maxValue(5, 10);
assertEq(Math.f16round(FLT16_MAX), FLT16_MAX);
assertEq(Math.f16round(65519), FLT16_MAX); // round-nearest rounds down to FLT16_MAX
assertEq(Math.f16round(65520), Infinity); // no longer rounds down to FLT16_MAX
/*********************************************************
/******* Tests on denormalizations and roundings *********
/********************************************************/
function minValue(exponentWidth, significandWidth) {
return Math.pow(2, -(Math.pow(2, exponentWidth - 1) - 2) - significandWidth);
}
var DBL_MIN = Math.pow(2, -1074);
assertEq(DBL_MIN, Number.MIN_VALUE); // sanity check
// Too small for a float16
assertEq(Math.f16round(DBL_MIN), 0);
var FLT16_MIN = minValue(5, 10);
assertEq(Math.f16round(FLT16_MIN), FLT16_MIN);
assertEq(Math.f16round(FLT16_MIN / 2), 0); // halfway, round-nearest rounds down to 0 (even)
// FLT16_MIN + a small amount rounds up to FLT16_MIN
assertEq(Math.f16round(2.980232238769531911744490042422139897126953655970282852649688720703125e-8), FLT16_MIN);
assertEq(Math.f16round(-FLT16_MIN), -FLT16_MIN);
assertEq(Math.f16round(-FLT16_MIN / 2), -0); // halfway, round-nearest rounds up to -0 (even)
// -FLT16_MIN - a small amount rounds down to -FLT16_MIN
assertEq(Math.f16round(2.980232238769531911744490042422139897126953655970282852649688720703125e-8), FLT16_MIN);
// Test some constants from https://github.com/petamoriken/float16/
assertEq(Math.f16round(0.499994), 0.5);
assertEq(Math.f16round(1.337), 1.3369140625);
// This will round incorrectly if the implementation first rounds to Float32,
// then to Float16
assertEq(Math.f16round(1.00048828125000022204), 1.0009765625);
reportCompare(0, 0, "ok");