Merge: Abstract_text: Add a method to get string representation of float in scientifi...
authorJean Privat <jean@pryen.org>
Fri, 23 Aug 2019 13:25:19 +0000 (09:25 -0400)
committerJean Privat <jean@pryen.org>
Fri, 23 Aug 2019 13:25:19 +0000 (09:25 -0400)
Add a method `to_se` to get the string representation of float in scientific notation.
```
123.45.to_se == "1.2345e+02"
0.001234.to_se  == "1.234e-03"
10860460000.0.to_se == "1.086046e+10"
```
The to_se method have a precision between 1 to 6 max decimal. The precision is adapted in function of the number.

Add a `to_precision_size_with_format` and `to_precision_fill_with_format` method to get the representation in a specific given format

Pull-Request: #2785
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Jean Privat <jean@pryen.org>

lib/core/text/abstract_text.nit

index 0072ec8..1b4b383 100644 (file)
@@ -2029,25 +2029,76 @@ redef class Float
        # ~~~
        # assert 12.34.to_s       == "12.34"
        # assert (-0120.030).to_s == "-120.03"
+       # assert (-inf).to_s == "-inf"
+       # assert (nan).to_s == "nan"
        # ~~~
        #
        # see `to_precision` for a custom precision.
        redef fun to_s do
                var str = to_precision(3)
-               if is_inf != 0 or is_nan then return str
-               var len = str.length
+               return adapt_number_of_decimal(str, false)
+       end
+
+       # Return the representation of `self`, with scientific notation
+       #
+       # Adpat the number of decimals as needed from 1 to a maximum of 6
+       # ~~~
+       # assert 12.34.to_sci       == "1.234e+01"
+       # assert 123.45.to_sci.to_f.to_sci  == "1.2345e+02"
+       # assert 0.001234.to_sci  == "1.234e-03"
+       # assert (inf).to_sci == "inf"
+       # assert (nan).to_sci == "nan"
+       # ~~~
+       fun to_sci: String
+       do
+               var is_inf_or_nan = check_inf_or_nan
+               if is_inf_or_nan != null then return is_inf_or_nan
+               return adapt_number_of_decimal(return_from_specific_format("%e".to_cstring), true)
+       end
+
+       # Return the `string_number` with the adapted number of decimal (i.e the fonction remove the useless `0`)
+       # `is_expo` it's here to specifi if the given `string_number` is in scientific notation
+       private fun adapt_number_of_decimal(string_number: String, is_expo: Bool): String
+       do
+               # check if `self` does not need an adaptation of the decimal
+               if is_inf != 0 or is_nan then return string_number
+               var len = string_number.length
+               var expo_value = ""
+               var numeric_value = ""
                for i in [0..len-1] do
-                       var j = len-1-i
-                       var c = str.chars[j]
-                       if c == '0' then
-                               continue
-                       else if c == '.' then
-                               return str.substring( 0, j+2 )
-                       else
-                               return str.substring( 0, j+1 )
+                       var j = len - 1 - i
+                       var c = string_number.chars[j]
+                       if not is_expo then
+                               if c == '0' then
+                                       continue
+                               else if c == '.' then
+                                       numeric_value = string_number.substring( 0, j + 2)
+                                       break
+                               else
+                                       numeric_value = string_number.substring( 0, j + 1)
+                                       break
+                               end
+                       else if c == 'e' then
+                               expo_value = string_number.substring( j, len - 1 )
+                               is_expo = false
                        end
                end
-               return str
+               return numeric_value + expo_value
+       end
+
+       # Return a string representation of `self` in fonction if it is not a number or infinity.
+       # Return `null` if `self` is not a not a number or an infinity
+       private fun check_inf_or_nan: nullable String
+       do
+               if is_nan then return "nan"
+
+               var isinf = self.is_inf
+               if isinf == 1 then
+                       return "inf"
+               else if isinf == -1 then
+                       return  "-inf"
+               end
+               return null
        end
 
        # `String` representation of `self` with the given number of `decimals`
@@ -2062,19 +2113,9 @@ redef class Float
        # ~~~
        fun to_precision(decimals: Int): String
        do
-               if is_nan then return "nan"
-
-               var isinf = self.is_inf
-               if isinf == 1 then
-                       return "inf"
-               else if isinf == -1 then
-                       return  "-inf"
-               end
-
-               var size = to_precision_size(decimals)
-               var cstr = new CString(size + 1)
-               to_precision_fill(decimals, size + 1, cstr)
-               return cstr.to_s_unsafe(byte_length = size, copy = false)
+               var is_inf_or_nan = check_inf_or_nan
+               if is_inf_or_nan != null then return is_inf_or_nan
+               return return_from_specific_format("%.{decimals}f".to_cstring)
        end
 
        # Returns the hexadecimal (`String`) representation of `self` in exponential notation
@@ -2085,32 +2126,26 @@ redef class Float
        # ~~~
        fun to_hexa_exponential_notation: String
        do
-               var size = to_precision_size_hexa
+               return return_from_specific_format("%a".to_cstring)
+       end
+
+       # Return the representation of `self`, with the specific given c `format`.
+       private fun return_from_specific_format(format: CString): String
+       do
+               var size = to_precision_size_with_format(format)
                var cstr = new CString(size + 1)
-               to_precision_fill_hexa(size + 1, cstr)
+               to_precision_fill_with_format(format, size + 1, cstr)
                return cstr.to_s_unsafe(byte_length = size, copy = false)
        end
 
-       # Required string length to hold `self` with `nb` decimals
-       #
-       # The length does not include the terminating null byte.
-       private fun to_precision_size(nb: Int): Int `{
-               return snprintf(NULL, 0, "%.*f", (int)nb, self);
-       `}
-
-       # Fill `cstr` with `self` and `nb` decimals
-       private fun to_precision_fill(nb, size: Int, cstr: CString) `{
-               snprintf(cstr, size, "%.*f", (int)nb, self);
-       `}
-
-       # The lenght of `self` in exponential hexadecimal notation
-       private fun to_precision_size_hexa: Int`{
-               return snprintf(NULL, 0, "%a", self);
+       # The lenght of `self` in the specific given c `format`
+       private fun to_precision_size_with_format(format: CString): Int`{
+               return snprintf(NULL, 0, format, self);
        `}
 
-       # Fill `cstr` with `self` in exponential hexadecimal notation
-       private fun to_precision_fill_hexa(size: Int, cstr: CString) `{
-               snprintf(cstr, size, "%a", self);
+       # Fill `cstr` with `self` in the specific given c `format`
+       private fun to_precision_fill_with_format(format: CString, size: Int, cstr: CString) `{
+               snprintf(cstr, size, format, self);
        `}
 end