5
$\begingroup$

When you use SetPrecision to set the precision of say 1.1 you will get some seemingly random numbers padded before the rest will be padded by zeros. I assume this comes from the true machine precision representation of 1.1 having these hidden numbers.

When you use backticks you do get the more intuitive result of the expression only being padded with zeros. This seems more desirable to me since what I want is to input floats as close to exact as possible (i.e. I mean that I want 1.1 to be as close as possible to 11/10 etc.)

But how do I use the backtick as a function. The Fullform is just the backtick and gives no hints for how to use it in a function. Naive tries such as #`40& are not considered valid syntax.

I guess a function could be constructed by multiplying by some power of ten and then rounding to an exact number. Followed by dividing by the same power of ten and using N[#, prec]& on the result but this seems needlessly complicated.

(Alternatively, explain to me why trying to get the backtick result is a bad idea.)

$\endgroup$
  • 2
    $\begingroup$ The backtick is not an operation (a function) that can be applied to data. It is part of the syntax of numbers. No evaluation takes place when Mathematica reads an expression like 1.2`3. This text (i.e. code) is directly interpreted as a certain expression (an arbitrary precision number). $\endgroup$ – Szabolcs May 28 '18 at 15:35
  • 2
    $\begingroup$ In contrast, SetPrecision is a function, an operation that gets carried out. First 1.1 is interpreted as a machine precision number. Then it is being converted to a different expression, an arbitrary precision number. $\endgroup$ – Szabolcs May 28 '18 at 15:37
  • $\begingroup$ @Szabolcs, yes I sort of suspected that, but I'd also expect there to also be a functional equivalent (either pre-existing or one that can be written). Indeed it seems that N[FromDigits[RealDigits[#]],prec] works. $\endgroup$ – Kvothe May 28 '18 at 15:38
  • $\begingroup$ Can you show an application of what you want? Where would simply typing 1.1`30 not suffice? $\endgroup$ – John Doty May 28 '18 at 17:44
  • 1
    $\begingroup$ Kvothe, would it be correct to assume that you simply want a decimal number library and all the talk about setting precision is a deviation from the actual goal? $\endgroup$ – LLlAMnYP May 29 '18 at 10:37
7
$\begingroup$
SetPrecision[1.1, 30]

gives

1.10000000000000008881784197001

but

SetPrecision[Rationalize[1.1], 30]

gives

1.10000000000000000000000000000

So perhaps using Rationalize will work for you.

$\endgroup$
3
$\begingroup$

One good solution is

N[FromDigits[RealDigits[#]],prec]&

In the test cases I tried it will exactly match the backtick input.


Alternatively, one other approach is to build the backtick expression as a string and then convert it to an expression (thus avoiding that before we assign a value the syntax is invalid):

toHighPrecision[x_, prec_] := ToExpression[ StringSplit[ToString[x, InputForm], "`"][[1]] <> "`" <> ToString[prec]]

This also works as expected, see:

toHighPrecision[#, 50] & /@ {1.1, 1.10000006, 
  1.10000000000000000000000008, 1.10000000000000000000000007}

(We had to split the string because the InputForm might contain a backtick already because MMA interprets it to have a certain amount of precision.)


My initial attempt for this strategy,failed because, even if I held the x, I still could not avoid some conversion from being made. For example,

SetAttributes[toHighPrecision,HoldAll];
toHighPrecision[x_]:=ToExpression[StringTake[ToString[Hold[x]],{6,-2}]<>"`"<>ToString[prec]]

did not work for 1.10000000000000008. The solution was proposed by LLlAMnYP, I should have used ToString[#,Inputform] instead.


Lastly, you can first convert the Real to an Integer. If we do this with Rationalize as proposed by m_goldberg we get a nice looking result, but this comes at the disadvantage of some loss of accuracy. (Our previous test case will be wrongly converted to 11/10).


To compare try:

N[FromDigits[RealDigits[#]],50]&[1.10000000000000008]
N[Rationalize[#],50]&[1.10000000000000008]
ToExpression[StringTake[ToString[Hold[#]],{6,-2}]<>"`"<>ToString[50]]&[1.10000000000000008]
ToExpression[StringSplit[ToString[#, InputForm], "`"][[1]] <> "`" <> ToString[50]]

Why the RealDigits solution works and does not run into the problem of a conversion is not 100% clear to me.

$\endgroup$
  • $\begingroup$ Yes, but since x is evaluated as a machine number, you won't be able to use this function on a number like 1.10000000000000008 $\endgroup$ – LLlAMnYP May 28 '18 at 16:05
  • $\begingroup$ @LLlAMnYP, good point, thanks. I think I'll delete my answer then since m_goldberg's answer is better. $\endgroup$ – Kvothe May 28 '18 at 16:15
  • $\begingroup$ Multiple answers are welcome here $\endgroup$ – LLlAMnYP May 28 '18 at 16:17
  • 1
    $\begingroup$ I tried to work around this issue by holding the input: SetAttributes[toHighPrecision,HoldAll]; toHighPrecision[x_]:=ToExpression[StringTake[ToString[Hold[x]],{6,-2}]<>""<>ToString[prec]]` At no point is x allowed to evaluate and yet it does not work. Any idea why not? It seems MMA still secretly does some evaluation by just deciding how to represent the input as a Real in its inner workings. $\endgroup$ – Kvothe May 28 '18 at 16:42
  • 1
    $\begingroup$ @LLlAMnYP, in fact thinking about it for a moment (and checking it in MMA) Rationalize is not actually any better in this regard. It has the exact same issue (try Rationalizing your suggested input). Seeing how even Holding the input did not prevent it to being converted to a machine number it seems the issue might be impossible to overcome. $\endgroup$ – Kvothe May 28 '18 at 16:45
1
$\begingroup$

@LLlAMnYP, the goal is to enter numbers with a certain precision prec. The only problem is that entering all numbers in the format as required by Mathematica is really cumbersome. I want it to be assumed that all digits that are not given are exactly 0's. If I input 1.1 in the function I mean 1.1`prec. I don't know exactly what you mean, but if a "decimal number library" is a way to accomplish this then please expand on this. It also already seems that the function in my answer accomplishes exactly what I want (even though I don't exactly understand why). So practically I'm satisfied.

A proper response to this goes out of hand for a comment, so I'll expand here.

I want it to be assumed that all digits that are not given are exactly 0's.

This is sort of a definition for decimal arithmetic. You want numbers to be what they look like to you, and not to a computer.

1.1`prec, by the way, does not achieve that goal:

SetPrecision[1.1`5, 70]
1.100000000000000000000000000000000000001175494350822287507968736537222

Though it does give you a bunch more zeroes before imprecise digits show up. So, I conclude, the numbers are still stored in binary form. The output from using the backtick is simply tricking you into believing, that the number is truly an exact 1.1 because it is using more binary digits of precision.

You also state "Fullform is just the backtick and gives no hints for how to use it in a function". Let me try to draw a parallel with C/C++ where you can input numbers in several ways, e.g.

0x0ab13  // hexadecimal
0777     // octal
15805687280289L // long

but, of course, one cannot simply write

int toOctal(int n) {
  return 0n;
}

There is low-level code in the compiler that interprets the strings making up a program in a certain way, like interpreting the string as an octal number if it sees 0 after a whitespace and backticks in Mathematica serve a similar function. However, every function you use always has an intermediate step of converting the inputted decimal number to a binary number. Usually, the shortest sufficiently exact representation of that number does not show the trailing non-zero digits, so all looks well. But your end-goal is not clear to me, so I cannot advise you on how to proceed and whether you will eventually run into problems.

However, since we live with the decimal system and often count money as a fixed-point decimal number, we recognized the need to carry out exact calculations with decimal numbers, so just like there exist bignum libraries that can work with numbers much bigger than 64 bits, so there exist decimal libraries that store numbers as a decimal representation, not as the closest base-2 equivalent. Mathematica appears to have such a library. Please refer to the ComputerArithmetic package.

<<ComputerArithmetic`
$\endgroup$
  • $\begingroup$ Thanks. However, I think the ComputerArithmetic package won't contain the solution. 1.1prec accomplishes exactly what I want. I want 1.1prec to be 1.1 padded with zero's up to the precision prec. Of course if you then want to increase the precision as you are doing you will get random numbers showing up again, but only after prec digits. Maybe I should have been more clear that that is what I want, the given digits padded with 0's upto prec digits. This is also exactly what the backtick does. My only frustration was that I couldn't do that functionally. Which, using my answer I now can. $\endgroup$ – Kvothe May 29 '18 at 13:34
  • $\begingroup$ @Kvothe I see what you mean, but I don't understand your use case (I am quite intrigued). Normally, when you want something in a functional way, this is for use in some calculations, where you do not have control, like where you said when I say 1.1 I mean 11/10. In these kinds of calculations you might get some machine-precise value like 3.3000000000000003 which would then be converted by the function to 3.30000000000000000000000000000 depending on whether you use inputform or not. Wait, you actually wanted 3.300000000000000300000000000000? Damn, that's another edge case. $\endgroup$ – LLlAMnYP May 29 '18 at 13:44
  • $\begingroup$ Using your suggestion to use InputForm in ToString I also managed to get the to String conversion method working. Thank you for your help. I hope you don't mind I included it in my answer. $\endgroup$ – Kvothe May 29 '18 at 14:00
  • $\begingroup$ @Kvothe not at all, a diversity of answers is good. Since ComputerArithmetic is suboptimal, there isn't really yet an established way to mean decimal numbers as they are, rather than as they are represented internally, so any contribution is meaningful. $\endgroup$ – LLlAMnYP May 29 '18 at 14:06
  • $\begingroup$ @Kvothe but take heed of my warning about 3.3: try your ToString[#,InputForm] approach like toHighPrecision[1.1 + 2.2, 50] and you'll see what I mean. $\endgroup$ – LLlAMnYP May 29 '18 at 14:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.