import 'dart:math' as math; class TestCase { const TestCase(this.value, this.expectation); final double value; final String expectation; } const List test = const [ const TestCase(0.0, '0.0'), const TestCase(-0.0, '-0.0'), const TestCase(double.INFINITY, 'Infinity'), const TestCase(-double.INFINITY, '-Infinity'), const TestCase(double.NEGATIVE_INFINITY, '-Infinity'), const TestCase(double.NAN, 'NaN'), const TestCase(double.MAX_FINITE, '1.798e+308'), // 1.7976931348623157e+308 const TestCase(double.MIN_POSITIVE, '4.941e-324'), // 5e-324 const TestCase(1.0, '1.0'), const TestCase(2.0, '2.0'), const TestCase(100.0, '100.0'), const TestCase(1234567890123456789.0, '1.235e+18'), const TestCase(0.00000000000000000000000000000000001, '1.000e-35'), const TestCase(123.138975987, '123.139\u2026'), const TestCase(123.1, '123.1'), const TestCase(123.100000001, '123.1\u2026'), const TestCase(123.1000000001, '123.1\u2026'), const TestCase(0.123456789, '0.123\u2026'), const TestCase(10000.0, '10000.0'), const TestCase(1000.0, '1000.0'), const TestCase(100.0, '100.0'), const TestCase(10.0, '10.0'), const TestCase(1.0, '1.0'), const TestCase(10000.1, '10000.1'), const TestCase(1000.1, '1000.1'), const TestCase(100.1, '100.1'), const TestCase(10.1, '10.1'), const TestCase(0.1, '0.1'), const TestCase(0.01, '0.01'), const TestCase(0.001, '0.001'), const TestCase(0.0001, '0.0001'), const TestCase(0.00001, '0.00001'), const TestCase(0.000001, '0.000001'), const TestCase(0.0000001, '0.0000001'), const TestCase(0.00000001, '0.00000001'), const TestCase(0.000000001, '0.000000001'), const TestCase(0.0000000001, '1.000e-10'), const TestCase(0.00000000001, '1.000e-11'), const TestCase(0.000000000001, '1.000e-12'), const TestCase(0.0000000000001, '1.000e-13'), const TestCase(0.00000000000001, '1.000e-14'), const TestCase(0.000000000000001, '1.000e-15'), const TestCase(0.11, '0.11'), const TestCase(0.101, '0.101'), const TestCase(0.1001, '0.1\u2026'), const TestCase(0.10001, '0.1\u2026'), const TestCase(0.100001, '0.1\u2026'), const TestCase(0.1000001, '0.1\u2026'), const TestCase(0.10000001, '0.1\u2026'), const TestCase(0.100000001, '0.1\u2026'), const TestCase(0.1000000001, '0.1\u2026'), const TestCase(0.010000000001, '0.01\u2026'), const TestCase(0.0010000000001, '0.001\u2026'), const TestCase(0.00010000000001, '0.0001\u2026'), const TestCase(0.000010000000001, '0.00001\u2026'), const TestCase(0.0000010000000001, '0.000001\u2026'), const TestCase(0.00000010000000001, '0.0000001\u2026'), const TestCase(0.000000010000000001, '0.00000001\u2026'), const TestCase(0.0000000010000000001, '0.000000001\u2026'), const TestCase(0.00000000010000000001, '1.000e-10'), const TestCase(0.000000000010000000001, '1.000e-11'), const TestCase(0.0000000000010000000001, '1.000e-12'), const TestCase(0.00000000000010000000001, '1.000e-13'), const TestCase(0.000000000000010000000001, '1.000e-14'), const TestCase(0.0000000000000010000000001, '1.000e-15'), const TestCase(1.1, '1.1'), const TestCase(1.01, '1.01'), const TestCase(1.001, '1.001'), const TestCase(1.0001, '1.0001'), const TestCase(1.00001, '1.00001'), const TestCase(1.000001, '1.000001'), const TestCase(1.0000001, '1.0000001'), const TestCase(1.00000001, '1.00000001'), const TestCase(1.000000001, '1.000000001'), const TestCase(1.0000000001, '1.0\u2026'), const TestCase(1.00000000001, '1.0\u2026'), const TestCase(1.000000000001, '1.0\u2026'), const TestCase(1.0000000000001, '1.0\u2026'), const TestCase(1.00000000000001, '1.0\u2026'), const TestCase(1.000000000000001, '1.0\u2026'), const TestCase(20000.2, '20000.2'), const TestCase(2000.2, '2000.2'), const TestCase(200.2, '200.2'), const TestCase(20.2, '20.2'), const TestCase(2.2, '2.2'), const TestCase(0.2, '0.2'), const TestCase(0.02, '0.02'), const TestCase(0.002, '0.002'), const TestCase(0.0002, '0.0002'), const TestCase(0.00002, '0.00002'), const TestCase(0.000002, '0.000002'), const TestCase(50000.5, '50000.5'), const TestCase(5000.5, '5000.5'), const TestCase(500.5, '500.5'), const TestCase(50.5, '50.5'), const TestCase(5.5, '5.5'), const TestCase(0.5, '0.5'), const TestCase(0.05, '0.05'), const TestCase(0.005, '0.005'), const TestCase(0.0005, '0.0005'), const TestCase(0.00005, '0.00005'), const TestCase(0.000005, '0.000005'), const TestCase(0.25, '0.25'), const TestCase(0.125, '0.125'), const TestCase(0.0009765625, '0.000977\u2026'), // 1/2^10 const TestCase(123456789.0, '123456789.0'), const TestCase(12345678.9, '12345678.9'), const TestCase(1234567.89, '1234567.89'), const TestCase(123456.789, '123456.789'), const TestCase(12345.6789, '12345.679\u2026'), const TestCase(1234.56789, '1234.568\u2026'), const TestCase(123.456789, '123.457\u2026'), const TestCase(12.3456789, '12.346\u2026'), const TestCase(1.23456789, '1.235\u2026'), const TestCase(0.123456789, '0.123\u2026'), const TestCase(0.9, '0.9'), const TestCase(0.89, '0.89'), const TestCase(0.789, '0.789'), const TestCase(0.6789, '0.679\u2026'), const TestCase(0.56789, '0.568\u2026'), const TestCase(0.456789, '0.457\u2026'), const TestCase(0.3456789, '0.346\u2026'), const TestCase(0.23456789, '0.235\u2026'), const TestCase(0.123456789, '0.123\u2026'), const TestCase(1.23456e-18, '1.235e-18'), const TestCase(1.23456e-19, '1.235e-19'), const TestCase(1.23456e-20, '1.235e-20'), const TestCase(1.23456e-21, '1.235e-21'), const TestCase(1.23456e-22, '1.235e-22'), const TestCase(1.23456e+18, '1.235e+18'), const TestCase(1.23456e+19, '1.235e+19'), const TestCase(1.23456e+20, '1.235e+20'), const TestCase(1.23456e+21, '1.235e+21'), const TestCase(1.23456e+22, '1.235e+22'), ]; // We switch to exponential notation aggressively (|v|>1e9 or |v|<1e-9) // because trying to read numbers with that many digits isn't pleasant. const double _kTooSmall = 1e-9; const double _kTooBig = 1e9; // We only bother with nine decimal places because beyond that you start to see // the limitations of binary representations of decimal floating point literals. // For example, 0.1 becomes 0.10000000000000000555. By truncating it at nine we // can pretend that 0.1 is really a precise 0.1, while still showing the details // of 0.000001, etc. const int _kDecimalPlaces = 9; String converter(double value) { if (value.isInfinite || value.isNaN || value == 0.0) return value.toString(); if (value.abs() < _kTooSmall || value.abs() > _kTooBig) return value.toStringAsExponential(3); if (value.toInt() == value) return value.toStringAsFixed(1); String s = value.toStringAsFixed(_kDecimalPlaces); assert(!s.contains('e')); assert(s.contains('.')); final int positionOfFirstDecimal = s.indexOf('.') + 1; int index = 0; while (positionOfFirstDecimal + index < s.length && s[positionOfFirstDecimal + index] == '0') index += 1; String result = value.toStringAsFixed(math.min(index + 3, _kDecimalPlaces)); assert(!result.contains('e')); assert(result.contains('.')); int length = result.length; while (result[length - 1] == '0') length -= 1; if (length == positionOfFirstDecimal) length += 1; result = result.substring(0, length); if (double.parse(result) == value) return result; return '$result\u2026'; } void main() { test.forEach((TestCase current) { String s = converter(current.value); if (s != current.expectation) print('FAILURE: ${current.value} became "$s"; expected "${current.expectation}"'); }); }