sdk icon indicating copy to clipboard operation
sdk copied to clipboard

add a method to produce engineering notation from a double value (double.toStringAsEngineering)

Open alihassan143 opened this issue 3 years ago • 17 comments

alihassan143 avatar Dec 14 '21 11:12 alihassan143

It is supported:

void main() {
  print(1e2);  // 100.0
  print(1e-2); // 0.01
}

mraleph avatar Dec 14 '21 11:12 mraleph

not its not engineering notation is all values in 10 power always be the multiple of 3

alihassan143 avatar Dec 14 '21 12:12 alihassan143

@alihassan143 do you want engineering notation for formatting? e.g. toStringAsExponential variant that just uses multiples of 3 exponents?

mraleph avatar Dec 14 '21 12:12 mraleph

no try this its not working

alihassan143 avatar Dec 15 '21 04:12 alihassan143

the to stringasExponential() did not give the right answer as expected you can try different values the 5.9e+10 wheren E+10 is not the multiple of three the multiple of three is e+9

alihassan143 avatar Dec 15 '21 04:12 alihassan143

@alihassan143 yes, toStringAsExponential does not produce an engineering notation. That's why I asked if you are requesting an alternative to toStringAsExponential that would produce an engineering notation, i.e. you are asking for Dart to add double.toStringAsEngineering which would return string representing the given double value in an engineering notation?

The title of the issue that you have created is extremely unclear - so I am just trying to figure out what exactly you are requesting.

mraleph avatar Dec 15 '21 08:12 mraleph

yes i want double to to add double.toStringAsEngineering so i did not have to run function to create engineering notation form doubel

alihassan143 avatar Dec 15 '21 09:12 alihassan143

/cc @lrhn for an opinion

mraleph avatar Dec 15 '21 09:12 mraleph

So, "engineering notation" is like exponential/scientific notation, except that the exponential must be a multiple of 3, so you can have up to three digits to the left of the decimal point.

The main reason we don't have any other functionality than what is already there, is that that's all JavaScript provides for us.

We can definitely do some text-manipulation on the result of d.toExponential() afterwards, but we don't have a way, in JavaScript, to go directly to the engineering notation. That will make it slow compared to toExponential. (The VM might be able to twiddle the existing scientific notation generation code, after all, the digits are all the same, the only difference is the exponent and the position of the decimal point. Which is also why string manipulation can work.)

I'd probably just write one myself. Maybe:

extension EngineeringNotation on double {
  String toStringAsEngineering() {
    var expString = this.toStringAsExponential();
    var eIndex = expString.lastIndexOf("e");
    if (eIndex < 0) return expString; // Not exponential.
    var expIndex = eIndex + 1;
    if (expString.startsWith("+", expIndex)) expIndex += 1;
    var exponent = int.parse(expString.substring(expIndex));
    
    var shift = exponent % 3; // 0, 1 or 2.
    if (shift == 0) return expString; // Already multiple of 3
    exponent -= shift;
    var dotIndex = expString.indexOf(".");
    int integerEnd;
    int fractionalStart;
    if (dotIndex < 0) {
      integerEnd = eIndex;
      fractionalStart = eIndex;
    } else {
      integerEnd = dotIndex;
      fractionalStart = dotIndex + 1;
    }
    var preDotValue = expString.codeUnitAt(integerEnd - 1) ^ 0x30;
    while (shift > 0) {
      shift--;
      preDotValue *= 10;
      if (fractionalStart < eIndex) {
        preDotValue += expString.codeUnitAt(fractionalStart++) ^ 0x30;
      }
    }
    return "${integerEnd > 1 ? '-' : ''}$preDotValue."
        "${expString.substring(fractionalStart, eIndex)}e${exponent >= 0 ? '+' : ''}$exponent";
  }
}

(I can definitely optimize the heck out of that if necessary, but is it necessary?)

lrhn avatar Dec 15 '21 17:12 lrhn

yes please do it i also write the same function for that to show double in engineering notation but other languages provide default feature of that

alihassan143 avatar Dec 15 '21 17:12 alihassan143

String floatToEngineering(double x) { int exp = 0, sign = 1; if (x < 0.0) { x = -x; sign = -sign; } while (x >= 1000.0) { x /= 1000.0; exp += 3; } while (x < 1.0) { x *= 1000.0; exp -= 3; } if (sign < 0) x = -x; return "${x.toStringAsFixed(4)}" + "e+" + "$exp"; } that is my function which is quite fast

alihassan143 avatar Dec 15 '21 17:12 alihassan143

Doing multiplication and division by 1000 can lose precision on doubles, so to avoid that, you'd want to use the same, or similar, code that already does the toStringAsExponential. It's guaranteed to not lose precision and give a result string which is closer to the actual double value than to any other (so parsing pack using double.parse gives the same result back).

You'll only lose precision if your numbers have a large number of significant bits, and the distance to zero is large, so there will be multiple multiplications or divisions, so it'll work for a large number of actual values. That's not the bar for platform libraries, though. They should work correctly for all values.

Still, I'm sure I can do something in JavaScript (maybe round-trip the string through an Uint8List so I can move the . by one or two positions), and likely modify the native toStringAsExponential to choose an exponent which is a power of 3.

The question is whether it's worth adding to the platform libraries (we've made it so far without), and whether it really belongs in a general number formatting library like package:intl.

lrhn avatar Dec 15 '21 17:12 lrhn

intl did not support engineering notation please add best funtions in dart please it save my lot of time

alihassan143 avatar Dec 15 '21 17:12 alihassan143

intl did not support engineering notation please add best funtions in dart please it save my lot of time

Try NumberFormat('###.##E+00'). The three # before the . causes the exponent to be a multiple of 3.

As you can see from the following, Intl can perhaps achieve what you require. There is not one 'engineering' format, but many:

import 'package:intl/intl.dart';
import 'dart:math' as math;

void main() {
  demo('###.##E+00');
  demo('##0.0#E0');
}

void demo(String pattern) {
  final nf = NumberFormat(pattern);
  print("--- '$pattern' ---");
  final values = [
    0,
    for (final e in [-9, -8, -7, -1, 0, 1, 2, 3, 4, 5, 6])
      1.23456 * math.pow(10, e)
  ];
  for (final value in values) {
    print(nf.format(value));
  }
}

Result:

--- '###.##E+00' ---
0E+00
1.23E-09
12.35E-09
123.46E-09
123.46E-03
1.23E+00
12.35E+00
123.46E+00
1.23E+03
12.35E+03
123.46E+03
1.23E+06
--- '##0.0#E0' ---
0.0E0
1.23E-9
12.35E-9
123.46E-9
123.46E-3
1.23E0
12.35E0
123.46E0
1.23E3
12.35E3
123.46E3
1.23E6

@mosuem - some things could be better for users trying to get an engineering format. (1) a default engineering format would help many users get started (2) there is no discussion in the documentation that the exponent is controlled like this from the format - perhaps there should be an example of a custom engineering-like format (3) even the helper constructors have indirect and hard to understand descriptions - the documentation talks about things like "the locale's XXX_PATTERN' etc without an example of what this might mean.

rakudrama avatar May 10 '23 11:05 rakudrama

Sorry if this is off-topic, but does anyone have any suggestions for the reverse? That is, for parsing a string in scientific notation to a double? I'm using string manipulation, but I had hoped that there were a core or otherwise popular package for a accomplishing the task.

sneurlax avatar Feb 13 '24 06:02 sneurlax

@sneurlax Parsing a fixed format number can be achieved using https://pub.dev/documentation/convert/latest/convert/FixedDateTimeFormatter-class.html. For locale-dependent strings, we discourage parsing, as it is very brittle.

mosuem avatar Feb 13 '24 09:02 mosuem

@sneurlax I would suggest simply using double.parse or double.tryParse. This will handle most of the examples from this thread, and will parse, say, 12e+08 and +1.2E9 as the same number.

If you need to reject one of these as not in the right format, then, as @mosuem says, the more brittle Intl parsing can be used.

If you mean something else by 'scientific notation' — something that has elements of layout such as 1.2×109 — then I don't believe that Intl can generate or parse numbers with different kinds of digits (normal digits vs superscripts).

rakudrama avatar Feb 13 '24 19:02 rakudrama