Skip to content

Latest commit

 

History

History
137 lines (91 loc) · 6.07 KB

FS-1142-Extended-numeric-literal.md

File metadata and controls

137 lines (91 loc) · 6.07 KB

F# RFC FS-1142 - Extended numeric literal

The design suggestion Underscores in numeric literals after prefix and before suffix, Extend custom numeric types to support floating point literals and Hexadecimal, octal and binary custom numeric literals has been marked "approved in principle".

This RFC covers the detailed proposal for this suggestion.

Summary

This RFC will allow the following things:

  • Underscores in numeric literals after prefix and before suffix like 0x_1 or 1_l or mixed them up like 0x_1_l.
  • Hexadecimal, octal, binary and floating point custom numeric literals like 0x1I or 1.0G

Motivation

Make the language more consistent and easier to read. Enhance the custom numeric literals feature by supporting not only integers but also floats.

Detailed design

  1. Underscores can now be placed after prefix (0x, 0o, 0b) and before suffix (eg. UL, y, m, f). These are forbidden previously.

    let pi1 = 3_.1415F      // Invalid cannot put underscores adjacent to a decimal point
    let pi2 = 3._1415F      // Invalid cannot put underscores adjacent to a decimal point
    let socialSecurityNumber1 = 999_99_9999_L         // OK (Invalid *previously*)
    
    let x1 = _52              // This is an identifier, not a numeric literal
    let x2 = 5_2              // OK (decimal literal)
    let x3 = 52_              // Invalid cannot put underscores at the end of a literal
    let x4 = 5_______2        // OK (decimal literal)
    
    let x5 = 0_x52            // Invalid cannot put underscores in the 0x radix prefix
    let x6 = 0x_52            // OK (Invalid *previously*)
    let x7 = 0x5_2            // OK (hexadecimal literal)
    let x8 = 0x52_            // Invalid cannot put underscores at the end of a number
    
    // In contrast to Java, literals with leading zeros are decimal in F#.
    let x9 = 0_52             // OK (decimal literal)
    let x10 = 05_2            // OK (decimal literal)
    let x11 = 052_            // Invalid cannot put underscores at the end of a number
    
    // To create an octal literal, prefix it with '0o' similar to hexadezimal literals. The same rules apply:
    let x12 = 0_o52            // Invalid cannot put underscores in the 0o radix prefix
    let x13 = 0o_52            // OK (Invalid *previously*)
    let x14 = 0o5_2            // OK (octal literal)
    let x15 = 0o52_            // Invalid cannot put underscores at the end of a number
  2. Allow number prefix (0x, 0o, 0b) before integer custom numeric literals.

    let x1 = 0x123I        // big int (291)
    let x2 = 0o123I        // big int (83)
    let x3 = 0b1010I       // big int (10)

    The number prefix will not be removed from the string sended to the FromString of the numeric literal module. This may be a break change.

    module NumericLiteralG =
      let FromString s = s
    
    let x4 = 0x_99_9999_9999_9999_9999G    // x4 will be "0x999999999999999999"
  3. Allow floating point custom numeric literals.

    This will require the numeric literal module contains two new functions:

    val FromFloat: float -> 'CustomNumber
    val FromFloatString: string -> 'CustomNumber

    According to this comment,

    • When the literal has <= 15 significant figures1 and its exponent is within -300 to 300, the compiler will call FromFloat with the parsed float number
    • Or the compiler will call FromFloatString with the original string
    module NumericLiteralG =
      let FromFloat (s: float) = s
      let FromFloatString (s: string) = s
    
    let x1 = 123.456e-10G    // x1: float = 1.23456e-08
    let x2 = 123.456789123456789123e-10G    // x2: string = "123.456789123456789123e-10"

Drawbacks

  • Custom numeric literals module might harder to write.

  • May introduce break change.

Alternatives

  • For number prefix (0x, 0o, 0b) before integer custom numeric literals, we might introduce a new FromIntegerString to avoid the break change.
  • Or firstly parse it to bigint then ToString to obtain a literal without prefix.

Compatibility

Please address all necessary compatibility questions:

  • Is this a breaking change? Maybe

  • What happens when previous versions of the F# compiler encounter this design addition as source code? Can write numeric literal module with new functions in the source code, but cannot use these new numeric literal grammar.

  • What happens when previous versions of the F# compiler encounter this design addition in compiled binaries? Cannot use the new numeric literal grammar.

Pragmatics

Tooling

Please list the reasonable expectations for tooling for this feature, including any of these:

  • Colorization

    Might need to change the color schema of the numeric literals.

Unresolved questions

  • Should we introduce a new FromIntegerString or use any way to remove number prefix from custom integer literal string passed to FromString?

Footnotes

  1. The first none zero figure to last none zero figure, without the dot. Can be match by the Regex:

    ^-?0*(?<number>\d+\.?\d*?)0*(?:$|[eE][+-]?(?<exp>\d+))