Skip to content

Commit

Permalink
first version of toFixedPointString.ts and unit tests, #113
Browse files Browse the repository at this point in the history
  • Loading branch information
pixelzoom committed Jun 22, 2022
1 parent 58d526d commit d8e25fc
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 0 deletions.
1 change: 1 addition & 0 deletions js/dot-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* @author Sam Reid (PhET Interactive Simulations)
*/

import './toFixedPointStringTests.js';
import './BinPackerTests.js';
import './Bounds2Tests.js';
import './ComplexTests.js';
Expand Down
74 changes: 74 additions & 0 deletions js/toFixedPointString.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2022, University of Colorado Boulder

/**
* toFixedPointString is a version of Number.toFixed that avoids rounding problems and floating-point errors that exist
* in Number.toFixed and dot.Utils.toFixed. It converts a number of a string, then modifies that string based on the
* number of decimal places desired. It performs symmetric rounding based on only the 2 specific digits that should be
* considered when rounding. Values that are not finite are converted using Number.toFixed.
*
* See https://github.com/phetsims/dot/issues/113
*
* @author Chris Malley (PixelZoom, Inc.)
*/

import dot from './dot.js';

function toFixedPointString( value: number, decimalPlaces: number ): string {
assert && assert( isFinite( decimalPlaces ) && decimalPlaces >= 0 );

// If value is not finite, then delegate to Number.toFixed and return immediately.
if ( !isFinite( value ) ) {
return value.toFixed( decimalPlaces ); // eslint-disable-line bad-sim-text
}

// Convert the value to a string.
let stringValue = value.toString();

// Find the decimal point in the string.
const decimalPointIndex = stringValue.indexOf( '.' );

if ( decimalPointIndex !== -1 ) {

// If there is a decimal point...
const actualDecimalPlaces = stringValue.length - decimalPointIndex - 1;
if ( actualDecimalPlaces < decimalPlaces ) {

// There are not enough decimal places, so pad with 0's to the right of decimal point
for ( let i = 0; i < decimalPlaces - actualDecimalPlaces; i++ ) {
stringValue += '0';
}
}
else if ( actualDecimalPlaces > decimalPlaces ) {

// There are too many decimal places, so round symmetric.
const digit = parseInt( stringValue[ decimalPointIndex + decimalPlaces + 1 ], 10 );
const delta = ( digit >= 5 ) ? 1 : 0;
stringValue = stringValue.substring( 0, stringValue.length - ( actualDecimalPlaces - decimalPlaces ) );
if ( stringValue[ stringValue.length - 1 ] === '.' ) {
stringValue = stringValue.substring( 0, stringValue.length - 1 );
}
const lastDecimal = parseInt( stringValue[ stringValue.length - 1 ], 10 ) + delta;
stringValue = stringValue.replace( /.$/, lastDecimal.toString() );
}
}
else {

// There is no decimal point, so add a decimal point and pad with zeros.
if ( decimalPlaces > 0 ) {
stringValue += '.';
for ( let i = 0; i < decimalPlaces; i++ ) {
stringValue += '0';
}
}
}

// Remove negative sign from -0 values.
if ( parseFloat( stringValue ) === 0 && stringValue[ 0 ] === '-' ) {
stringValue = stringValue.substring( 1, stringValue.length );
}

return stringValue;
}

dot.register( 'toFixedPointString', toFixedPointString );
export default toFixedPointString;
20 changes: 20 additions & 0 deletions js/toFixedPointStringTests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2022, University of Colorado Boulder

/**
* QUnit tests for toFixedPointString.
*
* @author Chris Malley (PixelZoom, Inc.)
*/

import toFixedPointString from './toFixedPointString.js';

QUnit.module( 'toFixedPointString' );

QUnit.test( 'tests', assert => {
assert.equal( toFixedPointString( 35.855, 2 ), '35.86' );
assert.equal( toFixedPointString( 35.854, 2 ), '35.85' );
assert.equal( toFixedPointString( 0.005, 2 ), '0.01' );
assert.equal( toFixedPointString( 0.004, 2 ), '0.00' );
assert.equal( toFixedPointString( -0.005, 2 ), '-0.01' );
assert.equal( toFixedPointString( -0.004, 2 ), '0.00' );
} );

0 comments on commit d8e25fc

Please sign in to comment.