Skip to content

[API Proposal]: bigdecimal #83907

Closed as not planned
Closed as not planned
@peheje

Description

@peheje

Background and motivation

(originally posted here dotnet/fsharp#14687)
Describe the solution you'd like

F# is marketed as a language good for Data Science and ML. Examples:

"F# for Data Science and ML F# is used in a variety of ways ... data science, and machine learning. ... are a great fit for data science and machine learning"
https://dotnet.microsoft.com/en-us/languages/fsharp

"F# is an excellent solution for programmatic data science"
https://fsharp.org/guides/data-science/

"Data analytics machine learning in F#"
https://learn.microsoft.com/en-us/events/dotnetconf-focus-on-fsharp/getting-started-with-data-analytics-machine-learning-in-fsharp

I would argue that F# or rather the dotnet platform is missing big feature to support this, a "BigDecimal" with arbitrarily many digits for mathematical operations. For example I stumpled upon finding decimal-repeats in the reciprocal of primes, some of which happen after many thousands of decimals. Opening up my favorite language F# to get started, I was a bit disspointed. There's no easy way to do this on the dotnet platform that I know of, other than calling other libraries such as GMP or the like.

Here's some code examples in Kotlin/JVM and Python how easy it is to work with, and then my attempt to workaround using System.Numerics.BigInteger in the end.

import java.math.BigDecimal
import java.math.RoundingMode
import kotlin.system.measureTimeMillis

fun main() {
    var result: BigDecimal?
    val executionTime = measureTimeMillis {
        val prime = BigDecimal(60013)
        result = BigDecimal.ONE.divide(prime, 1000000, RoundingMode.DOWN)
    }
    println(result)
    println("Execution time: ${executionTime}ms")
}

Takes about 200 ms on my PC. Python seems almost instant, I'm guessing it calls GMP:

from decimal import *

getcontext().prec = 1000000
one = Decimal(1)
prime = Decimal(60013)
print(one / prime)

Doing it with bigint and multiply first works, takes around 1.5 seconds on my PC, but it's not very flexible to work with, if you really want a decimal number.

open System.Diagnostics

let prime = bigint 60013
let sw = Stopwatch.StartNew()
let zeros length = System.String('0', length)
let multiplier = bigint.Parse("1" + (zeros 1_000_000))
let ans = multiplier / prime
sw.Stop()
printfn "Execution time: %ims" sw.ElapsedMilliseconds

In conclusion.

Shouldn't F#/dotnet have a BigDecimal type?
Especially in the light of the data-science marketing.

API Proposal

I don't have a specific proposal, but the design could mimic bigint / System.Numerics.BigInteger: https://learn.microsoft.com/en-us/dotnet/api/system.numerics.biginteger?view=net-7.0

API Usage

Maybe something like this:

let prime = bigdecimal 60013
let ans = bigdecimal.Divide(bigdecimal.One, prime, decimals=1000000)

And printing a string of the output would be the 1 million decimals

0.0000166630563377934780797493876326795860896805692100044990252112042390815323346608234882442137536867012147368070251445520137303...

There's some questions about the API. How to do it? Like Python example setting a context? Or like JVM/example? Maybe a mix?

Alternative Designs

No response

Risks

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-System.Numericsneeds-further-triageIssue has been initially triaged, but needs deeper consideration or reconsideration

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions