# Disable promotion from Float64 to BigFloat

**URL:** <https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529>\
**Category:** General Usage\
**Created:** [June 1, 2025, 2:27pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529 "2025-06-01T14:27:24Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![iryabink](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/iryabink/32/217092_2.png) [@iryabink](https://discourse.julialang.org/u/iryabink)\
**Post date:** [June 1, 2025, 2:27pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/1 "2025-06-01T14:27:24Z")

</div>

Dear community,  
Recently, I’ve been trying to debug an arbitrary precision algorithms/formulae. I stumbled upon to the fact that it is by default users are allowed to have mixed precision input without any warnings, e.g.

> big(1.3)  
> 1.3000000000000000444089209850062616169452667236328125  
> gives you a BigFloat instance of _inexact_ const “1.3”

> 1.3big(1)  
> 1.3000000000000000444089209850062616169452667236328125

etc.

What I would like to have something

> big(1.3)  
> ERROR: InexactError: big(1.3)  
> Stacktrace:  
> […]

everywhere I create the instance of BigFloat from Float64

What I tried:

1. add a new `promotion_rule`

> julia\> import Base.promote\_rule  
> julia\> Base.promote\_rule(::Type{BigFloat}, ::Type{Float64}) = error(“promote\_rule”)  
> promote\_type(BigFloat, Float64)  
> ERROR: promote\_rule  
> Stacktrace:

but still  
julia\> big(1.3)  
1.3000000000000000444089209850062616169452667236328125  
julia\> BigFloat(1.3)  
1.3000000000000000444089209850062616169452667236328125

1. I also tried to overload `widen`, still no luck

What is the minimal set of definitions to guarantee every promotion Float64 to BigFloat is caught?

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [June 1, 2025, 2:51pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/2 "2025-06-01T14:51:20Z")

</div>

The way to do it is to use a string macro, like this: `big"1.3"`. This expression is parsed directly as a BigFloat without performance loss.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [June 1, 2025, 3:01pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/3 "2025-06-01T15:01:43Z")

</div>

You could also have done things like `big(13)/big(10)` or `big(13//10)`.

The root cause of your problem is that `1.3` is inherently inexact, so it doesn’t matter what function you pass it to afterwards.

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [June 1, 2025, 3:15pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/4 "2025-06-01T15:15:52Z")

</div>

I’m fairly certain the original poster is aware of _why_ `1.3` has limited precision and wants to find a way to turn all promotions and constructors from double precision to `BigFloat` into errors, in order to detect where their code has made incorrect assumptions.

---

<div class="post-metadata">

**Author:** ![iryabink](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/iryabink/32/217092_2.png) [@iryabink](https://discourse.julialang.org/u/iryabink)\
**Post date:** [June 1, 2025, 4:01pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/5 "2025-06-01T16:01:41Z")

</div>

Yes, exactly!

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [June 1, 2025, 4:08pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/6 "2025-06-01T16:08:37Z")

</div>

But that’s just the thing — `1.3` _is_ exact! It’s just not exactly the number you think.

---

<div class="post-metadata">

**Author:** ![VinceNeede](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vinceneede/32/215744_2.png) [@VinceNeede](https://discourse.julialang.org/u/VinceNeede)\
**Post date:** [June 1, 2025, 4:12pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/7 "2025-06-01T16:12:20Z")

</div>

I think `big` and `BigFloat` are not promotion but use the constructor directly. I’m not sure if this is possible but could you try to overload the constructor or the struct directly? (This should be possibly in Julia 1.12 beta). I would not suggest changing this since it could create unpredictable problems, but I’m not sure there is another way. Do you actually need this? Most of the codes usually use `AbstractFloat` to keep things generic. Just to be sure we are not going through the `XY` problem, could you share what are you actually doing?

---

<div class="post-metadata">

**Author:** ![araujoms](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/araujoms/32/217734_2.png) [@araujoms](https://discourse.julialang.org/u/araujoms)\
**Post date:** [June 1, 2025, 8:31pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/8 "2025-06-01T20:31:35Z")

</div>

You can just redefine the constructor:

```julia
function BigFloat(x::Float64)
    throw(error("Imprecise."))
end

```

---

<div class="post-metadata">

**Author:** ![iryabink](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/iryabink/32/217092_2.png) [@iryabink](https://discourse.julialang.org/u/iryabink)\
**Post date:** [June 1, 2025, 9:28pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/9 "2025-06-01T21:28:54Z")

</div>

> [@VinceNeede](#):
>
> Just to be sure we are not going through the `XY` problem, could you share what are you actually doing?

Simplest case: I’m converting the expression for the LDA DFT energy functional, which is  
E\_x[LDA] = -3/4cbrt(3/pi)\*\int\_\rho^4/3 dr

my density is given on a grid of `BigFloat`, an integral is converted into sum on a Gaussian quadrature. The question is how to ensure that numerical constant in front of the expression matches the precision of integration.

Among various possible expressions this gives you enhanced accuracy:

> -3//4_cbrt(big(3)/pi)_…

while, for example

> -3//4_cbrt(3/pi)_…

is not. Now imagine you’d like to convert many such expressions for different functionals. I don’t want even a single chance of loosening accuracy on the way.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [June 1, 2025, 9:56pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/10 "2025-06-01T21:56:58Z")

</div>

> [@araujoms](#):
>
> ```julia
> function BigFloat(x::Float64)
> 
> ```

For upcoming versions of Julia explicitly qualify the constructor to avoid a deprecation warning. Like `function Base.BigFloat`.

> [@araujoms](#):
>
> ```julia
> throw(error("Imprecise."))
> 
> ```

The `error` call will throw already, making the `throw` redundant.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [June 1, 2025, 10:09pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/11 "2025-06-01T22:09:22Z")

</div>

Just to be a pedantic grumpy old man, the conversion from Float64 to Big _is perfectly exact_.

`1.3` _is exactly equal_ to 1.3000000000000000444089209850062616169452667236328125. It’s a bit of a mouthful, but it’s precisely `5854679515581645//4503599627370496`. The fact that it can’t hit 13/10ths is unfortunate, but the inexact part happened _before_ you got to a BigFloat promotion. It happened before you got to `Float64`, in fact!

So then the question is: how do you prevent Julia from giving you the default `Float64`? I think the most common place for `Float64`s to result from integer inputs would be in `/`. And so I’d just shadow that to increase the precision as you’d prefer:

```julia
julia> x / y = Base.:/(big(x), big(y))
/ (generic function with 1 method)

julia> 2/3
0.6666666666666666666666666666666666666666666666666666666666666666666666666666695

```

Or you could make it spit out rationals for integer inputs:

```julia
julia> x::Integer / y::Integer = big(x) // big(y)
/ (generic function with 2 methods)

julia> 2/3
2//3

```

Note that this is just _shadowing_ the builtin definitions for `/` and it’ll only apply in your current module. This is far better than pirating promotion (which would affect everything).

There are a few other places where you’ll find integers yielding `Float64`s — like in negative powers and roots. But those can similarly be shadowed.

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [June 1, 2025, 10:22pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/12 "2025-06-01T22:22:46Z")

</div>

Just to note - this WILL break code relying on this function that you may inadvertently depend on. Don’t do this.

---

<div class="post-metadata">

**Author:** ![iryabink](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/iryabink/32/217092_2.png) [@iryabink](https://discourse.julialang.org/u/iryabink)\
**Post date:** [June 1, 2025, 10:23pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/13 "2025-06-01T22:23:30Z")

</div>

> [@mbauman](#):
>
> `1.3` _is exactly equal_ to 1.3000000000000000444089209850062616169452667236328125.

I appreciate your determination, but I insist that the displayed value of `1.3` after 16th decimal is a garbage taken from the stack/heap/whatever by `mpfr` library, these digits vary depending on your hardware.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [June 1, 2025, 10:24pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/14 "2025-06-01T22:24:13Z")

</div>

> [@iryabink](#):
>
> the displayed value of `1.3` after 16th decimal is a garbage taken from the stack/heap/whatever by `mpfr` library, these digits vary depending on your hardware.

If so, that’s a bug. Can you give an example of different platforms producing different results here? Might be due to some of the global state configuration that MPFR sadly relies on, or due to the width of the floating-point exponent? Or perhaps it’s due to Intel’s 80-bit FP?

Apart from that, seems like you misunderstood @mbauman. Note that he put `1.3` into a code block when writing that, drawing a distinction between the actual rational number, 1.3 = \frac{13}{10}, representable as, e.g., `13 // 10` in Julia, and `1.3::Float64`.

Compare:

```julia-repl
julia> setprecision(500) do
           BigFloat(13 // 10)
       end
1.2999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999998

julia> setprecision(500) do
           BigFloat(13 / 10)
       end
1.3000000000000000444089209850062616169452667236328125

```

---

<div class="post-metadata">

**Author:** ![iryabink](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/iryabink/32/217092_2.png) [@iryabink](https://discourse.julialang.org/u/iryabink)\
**Post date:** [June 1, 2025, 10:36pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/15 "2025-06-01T22:36:56Z")

</div>

13//10 is a different story. It is a rational number with a value

> 1.2(9)

in any floating point representation. So, 1.3 != 13//10.

Anyway, colleagues. Many thanks to all participants for the help!

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [June 1, 2025, 11:12pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/16 "2025-06-01T23:12:52Z")

</div>

> [@iryabink](#):
>
> ```julia
> julia> big(1.3)
> 1.3000000000000000444089209850062616169452667236328125
> 
> ```
> 
> I insist that the displayed value of `1.3` after 16th decimal is a garbage taken from the stack/heap/whatever by `mpfr` library, these digits vary depending on your hardware.

It does not, it’s just the exact value of the closest `Float64` representation of 1.3. If we add up the powers of 2 for each bit, we get the same digits minus the implicit leading 1.

```julia
julia> big(1.3)
1.3000000000000000444089209850062616169452667236328125

julia> sum(parse(Int, b)*(big(2.0)^e) for (e, b) in zip(-1:-1:-52, bitstring(1.3)[13:64]))
0.3000000000000000444089209850062616169452667236328125

```

If arbitrary precision floats were really just taking garbage bits (which would be crazy for such a widespread dependency), it’d be a miracle for a sum of 52 of them to give the same digits as a 53rd, or for the digits to be so consistent across multiple instances on possibly different processes on different machines. `Float64` and any binary numeric type can only be exact for sums of powers of 2, a strict subset of rational numbers. 1.3 = 1 + 3/10 is not in that subset, so it must be approximated. If we check the smaller adjacent `Float64` value, it is even farther from the unrepresentable 1.3, and the adjacent values differ by the least significant bit.

```julia
julia> big.([prevfloat(1.3), 1.3])
2-element Vector{BigFloat}:
 1.29999999999999982236431605997495353221893310546875
 1.3000000000000000444089209850062616169452667236328125

julia> log2(1.3 - prevfloat(1.3))
-52.0

```

`Float64` only prints 1.3 because it truncates more (note that `BigFloat` also truncates, a big tell is when the digits don’t end with a 5 as all negative powers of 2 do); it still has the same value as the `BigFloat` conversion:

```julia
julia> 1.3 == big(1.3)
true

```

In fact, if we lower the global instantiation precision of `BigFloat` to that of `Float64`, we get the same level of truncation:

```julia
julia> setprecision(53) do
         parse(BigFloat, "1.3")
       end
1.3

```

If rounding in printing isn’t your style, then the only real way out of the inexactness of binary representations (sums of powers of 2) of decimal numbers is to parse text to decimal representations (sums of powers of 2 and 5). Support isn’t too great, the decimal floating point DecFP.jl isn’t active, the arbitrary precision Decimals.jl admits it’s not as efficient as the more typical libmpdec wrappers, then there’s the aptly named FixedPointDecimals.jl . No idea how good these are in practice, I just got used to rounding and printing a long time ago and only tried Decimals a couple times before giving up because of the incomplete support.

---

<div class="post-metadata">

**Author:** ![iryabink](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/iryabink/32/217092_2.png) [@iryabink](https://discourse.julialang.org/u/iryabink)\
**Post date:** [June 2, 2025, 1:06pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/17 "2025-06-02T13:06:46Z")

</div>

OK, let me summarize for future readers.

> big(1.3)  
> 1.3000000000000000444089209850062616169452667236328125

1. Is indeed an exact representation of `Float64` of a constant 1.3. `big` copies its mantissa into 256bit (default) representation bit-by-bit (53 bits) and set the rest to 0b. This is the place where I got confused: I thought, an initial mantissa of `BigFloat` can be arbitrary. No, it is wrong, it is always set to 0b initially. No hardware dependencies.
2. Unfortunately, this is not what I wanted – a proper floating point representation of a rational 13//10, which is 1.2(9)

To track such unexpected conversions, I’d like to convert all such implicit conversions into errors.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [June 2, 2025, 1:12pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/18 "2025-06-02T13:12:13Z")

</div>

And did redefining `Base.BigFloat(::AbstractFloat)` or creating a local `BigFloat` constructor fully solve the issue? You did click ‘Solution’ on that post, but the later discussion and summary made this a bit unclear again.

---

<div class="post-metadata">

**Author:** ![iryabink](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/iryabink/32/217092_2.png) [@iryabink](https://discourse.julialang.org/u/iryabink)\
**Post date:** [June 2, 2025, 1:22pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/19 "2025-06-02T13:22:40Z")

</div>

> [@DNF](#):
>
> And did redefining `Base.BigFloat(::AbstractFloat)` or creating a local `BigFloat` constructor fully solve the issue?

I think so.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [June 2, 2025, 5:05pm UTC](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529/20 "2025-06-02T17:05:01Z")

</div>

In my opinion, you’re disallowing `Float64` → `BigFloat` in the wrong place and with a misleading message. As you summarized, the conversion from one binary numeral to a higher precision binary numeral is actually exact, so `error("Imprecise.")` or the more typical `InexactError` is incorrect. What you don’t want is people using the first binary numeral’s lower precision in the first place (converted to 200+ zeroed bits) in your algorithm, you want them to use an exact representation (string, `Rational`) or parse/convert that to a higher precision numeral for your algorithm. That suggests the check belongs in a preceding or preliminary step of your algorithm, not _globally._

If you disallow the exact conversion globally, then you also stop your users from using their code or any other package that needs it. You might have even broken something in or that will be in `Base`. This conversion exists for good reasons:

1. There are many decimal values that _are_ exactly represented in binary, and you just forced users to convert them away from `Float64` just for conversion to `BigFloat` for more precise operations. `big.(zeros(3))` no longer works, we need to do `parse.(BigFloat, string.(zeros(3)))`.

2. Many algorithms start with the lower precision, do an operation in a higher precision, then return to the lower precision. The larger initial approximation error is just part of the intent. Again, you just forced a lot of extra work on that.

Assuming you’re just doing this in your algorithm, another issue is that you’re also only addressing `Float64`. There’s the even less precise `Float16` and `Float32` in `Base`, and there can be an arbitrary number of lower precision `AbstractFloat` subtypes you don’t want as well as an arbitrary number of higher precision `AbstractFloat` subtypes you do want (including `BigFloat` → `BigFloat`). You can’t disallow an infinite subset of input types explicitly, so you should try specifying what input types you do allow. At this point, the inputs could be `Union{AbstractString, Rational, BigFloat}`, though you will probably just use `BigFloat` in the core algorithm because strings can’t support any arithmetic and `Rational` will automatically convert to `Float64` in many operations that usually don’t have rational outputs e.g. `asin(1//2)`.

[Next page](https://discourse.julialang.org/t/disable-promotion-from-float64-to-bigfloat/129529.md?page=2)
