Can somebody please explain the following behavior:
static void Main(string[] args)
{
checked
{
double d = -1d + long.MinValue; //this resolves at runtime to -9223372036854780000.00
//long obviousOverflow = -9223372036854780000; //compile time error, '-' cannot be applied to operand of tpye ulong -> this makes it obvious that -9223372036854780000 overflows a long.
double one = 1;
long lMax = (long)(one + long.MaxValue); // THROWS
long lMin = (long)(-one + long.MinValue); // THEN WHY DOES THIS NOT THROW?
}
}
I don't undersant why I'm not getting an OverFlowException in the last line of code.
UPDATE Updated code to make it obvious that checked does through when casting a double to long except in the last case.
You are calculating with double values (-1d). Floating point numbers do not throw on .NET. checked does not have influence on them in any way.
But the conversion back to long is influenced by checked. one + long.MaxValue does not fit into the range of double. -one + long.MinValue does fit into that range. The reason for that is that signed integers have more negative numbers than positive numbers. long.MinValue has no positve equivalent. That's why the negative version of your code happens to fit and the positive version does not fit.
The addition operation does not change anything:
Debug.Assert((double)(1d + long.MaxValue) == (double)(0d + long.MaxValue));
Debug.Assert((double)(-1d + long.MinValue) == (double)(-0d + long.MinValue));
The numbers we are calculating are outside of the range where double is precise. double can fit integers up to 2^53 precisely. We have rounding errors here. Adding one is the same as adding zero. Essentially, you are computing:
var min = (long)(double)(long.MinValue); //does not overflow
var max = (long)(double)(long.MaxValue); //overflows (compiler error)
The add operation is a red herring. It does not change anything.
Apparently there is some leeway in the conversion from double to long. If we run the following code:
checked {
double longMinValue = long.MinValue;
var i = 0;
while (true)
{
long test = (long)(longMinValue - i);
Console.WriteLine("Works for " + i++.ToString() + " => " + test.ToString());
}
}
It goes up to Works for 1024 => -9223372036854775808 before failing with an OverflowException, with the -9223372036854775808 value never changing for i.
If we run the code unchecked, no exception is thrown.
This behavior is not coherent with the documentation on explicit numeric conversions that says:
When you convert from a double or float value to an integral type, the
value is truncated. If the resulting integral value is outside the
range of the destination value, the result depends on the overflow
checking context. In a checked context, an OverflowException is
thrown, while in an unchecked context, the result is an unspecified
value of the destination type.
But as the example shows, the truncation doesn't occur immediately.
I assume this is the question:
I don't undersant why I'm not getting an OverFlowException in the last
line of code.
Have a look at this line (the 1d you are using is insignificant and can be removed, the only thing it provided was conversion to double):
var max = (long)(double)long.MaxValue;
It throws because the closest (I do not know the spec, so I won't go into what "closest" is here) double representation of int64.MaxValue is larger than the largest int64, ergo it cannot be converted back.
var min = (long)(double)long.MinValue;
For this line on the other hand the closest double representation is between int64.MinValue and 0 so it can be converted back to int64.
What I just said does not hold for all combinations of jitter, hardware etc, but I'm trying to explain what happens. Remember that in your case it is thrown because of the checked keyword, without it the jitter would just swallow it.
Also I would recommend you to have a look at BitConverter.GetBytes() to experiment what happens when you go from double to long and back with large number, also decimal and double is interesting :) (the byte representation is the only representation you can trust btw, don't use the debugger for precision when it comes to double)
Related
In my program, I get a FormatException with the message The value could not be parsed on the following line:
c = BigInteger.Parse("" + Math.Pow(b, 3));
The exception occurs when b = 99000, but if I manually replace b with 99000, the exception doesn't occur. I have also tested it with b = 99001, and other higher values but I don't get the error.
The underlying cause is that Math.Pow operates on doubles, not integral data types. By default, Double.ToString() returns only 15 digits of precision, and the double data type can only store a maximum of 17 digits of precision internally.
When b=100,000, b3 = 1,000,000,000,000,000 (16 digits), and the conversion to a string will return the result as 1E+15. BigInteger.Parse can only parse integral values (optional whitespace, followed by a sign, followed by 1 or more numbers), and will throw a FormatException when the string value "1E+15" is provided as an argument.
A solution is to avoid Math.Pow entirely, and to instead use BigInteger.Pow(BigInteger, int). This approach avoids conversions to double entirely, and can exponentiate arbitrarily large integers.
With this change, your code might resemble something like this:
BigInteger c;
for (BigInteger b = 1; b < 1000000; b++)
{
c = BigInteger.Pow(b, 3);
// c stores b^3 as a BigInteger
}
Weird ... This seems to work for me, and you were right about the string in parse.
BigInteger c;
long b = 9000;
c = BigInteger.Parse("" + Math.Pow(b, 3));
No errors thrown ...
As for codemonkeh comment about the ToString(), you could also do this
BigInteger d = new BigInteger(Math.Pow(b, 3));
Assuming you are not running in a loop that will create heaps of those ...
I was a looking at the source code of a project, and I noticed the following statement (both keyByte and codedByte are of type byte):
return (byte)(keyByte - codedByte);
I'm trying now to understand what would the result be in cases where keyByte is smaller than codedByte, which results in a negative integer.
After some experiments to understand the result of casting a negative integer which has a value in the range [-255 : -1], I got the following results:
byte result = (byte) (-6); // result = 250
byte result = (byte) (-50); // result = 206
byte result = (byte) (-17); // result = 239
byte result = (byte) (-20); // result = 236
So, provided that -256 < a < 0 , I was able to determine the result by:
result = 256 + a;
My question is: should I always expect this to be the case?
Yes, that will always be the case (i.e. it is not simply dependent on your environment or compiler, but is defined as part of the C# language spec). See http://msdn.microsoft.com/en-us/library/aa691349(v=vs.71).aspx:
In an unchecked context, the result is truncated by discarding any high-order bits that do not fit in the destination type.
The next question is, if you take away the high-order bits of a negative int between -256 and -1, and read it as a byte, what do you get? This is what you've already discovered through experimentation: it is 256 + x.
Note that endianness does not matter because we're discarding the high-order (or most significant) bits, not the "first" 24 bits. So regardless of which end we took it from, we're left with the least significant byte that made up that int.
Yes. Remember, there's no such thing as "-" in the domain of a .Net "Byte":
http://msdn.microsoft.com/en-us/library/e2ayt412.aspx
Because Byte is an unsigned type, it cannot represent a negative
number. If you use the unary minus (-) operator on an expression that
evaluates to type Byte, Visual Basic converts the expression to Short
first. (Note: substitute any CLR/.Net language for "Visual Basic")
ADDENDUM:
Here's a sample app:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestByte
{
class Program
{
static void Main(string[] args)
{
for (int i = -255; i < 256; i++)
{
byte b = (byte)i;
System.Console.WriteLine("i={0}, b={1}", i, b);
}
}
}
}
And here's the resulting output:
testbyte|more
i=-255, b=1
i=-254, b=2
i=-253, b=3
i=-252, b=4
i=-251, b=5
...
i=-2, b=254
i=-1, b=255
i=0, b=0
i=1, b=1
...
i=254, b=254
i=255, b=255
Here is an algorithm that performs the same logic as casting to byte, to help you understand it:
For positives:
byte bNum = iNum % 256;
For negatives:
byte bNum = 256 + (iNum % 256);
It's like searching for any k which causes x + 255k to be in the range 0 ... 255. There could only be one k which produces a result with that range, and the result will be the result of casting to byte.
Another way of looking at it is as if it "cycles around the byte value range":
Lets use the iNum = -712 again, and define a bNum = 0.
We shall do iNum++; bNum--; untill iNum == 0:
iNum = -712;
bNum = 0;
iNum++; // -711
bNum--; // 255 (cycles to the maximum value)
iNum++; // -710
bNum--; // 254
... // And so on, as if the iNum value is being *consumed* within the byte value range cycle.
This is, of course, just an illustration to see how logically it works.
This is what happens in unchecked context. You could say that the runtime (or compiler if the Int32 that you cast to Byte is known at compiletime) adds or subtracts 256 as many times as is needed until it finds a representable value.
In a checked context, an exception (or compiletime error) results. See http://msdn.microsoft.com/en-us/library/khy08726.aspx
Yes - unless you get an exception.
.NET defines all arithmetic operations only on 4 byte and larger data types. So the only non-obvious point is how converting an int to a byte works.
For a conversion from an integral type to another integral type, the result of conversion depends on overflow checking context (says the ECMA 334 standard, Section 13.2.1).
So, in the following context
checked
{
return (byte)(keyByte - codedByte);
}
you will see a System.OverflowException. Whereas in the following context:
unchecked
{
return (byte)(keyByte - codedByte);
}
you are guaranteed to always see the results that you expect regardless of whether you do or don't add a multiple of 256 to the difference; for example, 2 - 255 = 3.
This is true regardless of how the hardware represents signed values. The CLR standard (ECMA 335) specifies, in Section 12.1, that the Int32 type is a "32-bit two's-complement signed value". (Well, that also matches all platforms on which .NET or mono is currently available anyway, so one could almost guess that it would work anyway, but it is good to know that the practice is supported by the language standard and portable.)
Some teams do not want to specify overflow checking contexts explicitly, because they have a policy of checking for overflows early in development cycle, but not in released code. In these cases you can safely do byte arithmetic like this:
return (byte)((keyByte - codedByte) % 256);
Math.Ceiling returns double because double may store much bigger numbers.
However if i'm sure that int type is capable to store the result how should I convert? Is it safe to cast (int) Math.Ceiling(... ?
If you are sure that you do not cross the capacity of int, it should be perfectly safe to do
int myInt = (int)Math.Ceiling(...);
If you are not sure about the bound, you could go with long instead of int.
From C++ practices, I would use the following. It's guaranteed to get the correct result even when ceiling returns 99.99999...8 or 100.000000...1
var result = (int)(Math.Ceiling(value) + 0.5);
The code below should work too if you trust its implementation
var result = Convert.ToInt32(value);
If it's all about speed, then Math.Ceiling for Int inputs and output is quite slow. The fastest is an inline expression. 2.4 seconds vs 33 ms.
Warning: Only for positive value and divisor values.
A) Modulus
Here's one I came up with, that has obviously also been found by C/C++ developers before:
var ceilingResult = (value / divisor) + (value % divisor == 0 ? 0 : 1);
From my own benchmark of 10M iterations, Math.Ceiling takes ~2.4 seconds. Calling this expression inside a named function takes ~380 ms and having it as a direct inline expression takes ~33ms.
B) Simple arithmetic only
Also consider using the suggestion from #mafu
var ceilingResult = (value + divisor - 1) / divisor;
See this 470x upvoted C++ answer for reference and validation. Also https://stackoverflow.com/a/4175152/887092.
C) DivRem
While looking at this answer, https://stackoverflow.com/a/14878734/887092, I noticed the comment that reminded me about DivRem CPU instructions. See https://learn.microsoft.com/en-us/dotnet/api/system.math.divrem?view=netframework-4.8. Math.DivRem should get resolved down to such a CPU instruction.
var quotient = Math.DivRem(value, divisor, out long remainder);
var ceilingResult = quotient + (remainder == 0 ? 0 : 1);
[I have not tested this]. See https://stackoverflow.com/a/924160/887092, for potential edge cases (Where negative Int numbers are used)
Further optimisations might be possible for this - maybe with casting. In this answer, https://stackoverflow.com/a/924160/887092, an if-conditional statement is used - they are about the same.
Performance of the 3:
Modulus: Has two operations that are added, but also a conditional branch.
Arithmetic: Has some additional mathematical operations in a sequence.
DivRem: Builds on the Modulus approach. If C# does resolve Math.DivRem to a single CPU instruction, this might be faster. Further optimisations might also be possible.
I'm not sure how the two would perform on various architectures. But now you have options to explore.
If you would like Math.Floor for Int inputs and Output, it's even easier:
var floorResult = (value / divisor);
I'd go with
int x = (int)Math.Ceiling(0.9); // 1
If you are uncertain you can always put an if statement and check if the number you get back is highter then int.MaxValue
int oInt = Convert.ToInt32(Math.Ceiling(value));
since Math.Ceiling returns double and you want to convert it to int, use Convert Class. example:
double[] values= { Double.MinValue, -1.38e10, -1023.299, -12.98,
0, 9.113e-16, 103.919, 17834.191, Double.MaxValue };
int result;
foreach (double value in values)
{
try {
result = Convert.ToInt32(value);
Console.WriteLine("Converted the {0} value '{1}' to the {2} value {3}.",
value.GetType().Name, value,
result.GetType().Name, result);
}
catch (OverflowException) {
Console.WriteLine("{0} is outside the range of the Int32 type.", value);
}
}
// -1.79769313486232E+308 is outside the range of the Int32 type.
// -13800000000 is outside the range of the Int16 type.
// Converted the Double value '-1023.299' to the Int32 value -1023.
// Converted the Double value '-12.98' to the Int32 value -13.
// Converted the Double value '0' to the Int32 value 0.
// Converted the Double value '9.113E-16' to the Int32 value 0.
// Converted the Double value '103.919' to the Int32 value 104.
// Converted the Double value '17834.191' to the Int32 value 17834.
// 1.79769313486232E+308 is outside the range of the Int32 type.
We have an interesting problem were we need to determine the decimal precision of a users input (textbox). Essentially we need to know the number of decimal places entered and then return a precision number, this is best illustrated with examples:
4500 entered will yield a result 1
4500.1 entered will yield a result 0.1
4500.00 entered will yield a result 0.01
4500.450 entered will yield a result 0.001
We are thinking to work with the string, finding the decimal separator and then calculating the result. Just wondering if there is an easier solution to this.
I think you should just do what you suggested - use the position of the decimal point. Obvious drawback might be that you have to think about internationalization yourself.
var decimalSeparator = NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
var position = input.IndexOf(decimalSeparator);
var precision = (position == -1) ? 0 : input.Length - position - 1;
// This may be quite unprecise.
var result = Math.Pow(0.1, precision);
There is another thing you could try - the Decimal type stores an internal precision value. Therefore you could use Decimal.TryParse() and inspect the returned value. Maybe the parsing algorithm maintains the precision of the input.
Finally I would suggest not to try something using floating point numbers. Just parsing the input will remove any information about trailing zeros. So you have to add an artifical non-zero digit to preserve them or do similar tricks. You might run into precision issues. Finally finding the precision based on a floating point number is not simple, too. I see some ugly math or a loop multiplying with ten every iteration until there is no longer any fractional part. And the loop comes with new precision issues...
UPDATE
Parsing into a decimal works. Se Decimal.GetBits() for details.
var input = "123.4560";
var number = Decimal.Parse(input);
// Will be 4.
var precision = (Decimal.GetBits(number)[3] >> 16) & 0x000000FF;
From here using Math.Pow(0.1, precision) is straight forward.
UPDATE 2
Using decimal.GetBits() will allocate an int[] array. If you want to avoid the allocation you can use the following helper method which uses an explicit layout struct to get the scale directly out of the decimal value:
static int GetScale(decimal d)
{
return new DecimalScale(d).Scale;
}
[StructLayout(LayoutKind.Explicit)]
struct DecimalScale
{
public DecimalScale(decimal value)
{
this = default;
this.d = value;
}
[FieldOffset(0)]
decimal d;
[FieldOffset(0)]
int flags;
public int Scale => (flags >> 16) & 0xff;
}
Just wondering if there is an easier
solution to this.
No.
Use string:
string[] res = inputstring.Split('.');
int precision = res[1].Length;
Since your last examples indicate that trailing zeroes are significant, I would rule out any numerical solution and go for the string operations.
No, there is no easier solution, you have to examine the string. If you convert "4500" and "4500.00" to numbers, they both become the value 4500 so you can't tell how many non-value digits there were behind the decimal separator.
As an interesting aside, the Decimal tries to maintain the precision entered by the user. For example,
Console.WriteLine(5.0m);
Console.WriteLine(5.00m);
Console.WriteLine(Decimal.Parse("5.0"));
Console.WriteLine(Decimal.Parse("5.00"));
Has output of:
5.0
5.00
5.0
5.00
If your motivation in tracking the precision of the input is purely for input and output reasons, this may be sufficient to address your problem.
Working with the string is easy enough.
If there is no "." in the string, return 1.
Else return "0.", followed by n-1 "0", followed by one "1", where n is the length of the string after the decimal point.
Here's a possible solution using strings;
static double GetPrecision(string s)
{
string[] splitNumber = s.Split('.');
if (splitNumber.Length > 1)
{
return 1 / Math.Pow(10, splitNumber[1].Length);
}
else
{
return 1;
}
}
There is a question here; Calculate System.Decimal Precision and Scale which looks like it might be of interest if you wish to delve into this some more.
I see that, in C#, rounding a decimal, by default, uses MidpointRounding.ToEven. This is expected, and is what the C# spec dictates. However, given the following:
A decimal dVal
A format string sFmt that, when passed in to dVal.ToString(sFmt), will result in a string containing a rounded version of dVal
...it is apparent that decimal.ToString(string) returns a value rounded using MidpointRounding.AwayFromZero. This would appear to be a direct contradiction of the C# spec.
My question is this: is there a good reason this is the case? Or is this just an inconsistency in the language?
Below, for reference, I've included some code that writes to console an assortment of rounding operation results and decimal.ToString(string) operation results, each on every value in an array of decimal values. The actual outputs are embedded. After that, I've included a relevant paragraph from the C# Language Specification section on the decimal type.
The example code:
static void Main(string[] args)
{
decimal[] dArr = new decimal[] { 12.345m, 12.355m };
OutputBaseValues(dArr);
// Base values:
// d[0] = 12.345
// d[1] = 12.355
OutputRoundedValues(dArr);
// Rounding with default MidpointRounding:
// Math.Round(12.345, 2) => 12.34
// Math.Round(12.355, 2) => 12.36
// decimal.Round(12.345, 2) => 12.34
// decimal.Round(12.355, 2) => 12.36
OutputRoundedValues(dArr, MidpointRounding.ToEven);
// Rounding with mr = MidpointRounding.ToEven:
// Math.Round(12.345, 2, mr) => 12.34
// Math.Round(12.355, 2, mr) => 12.36
// decimal.Round(12.345, 2, mr) => 12.34
// decimal.Round(12.355, 2, mr) => 12.36
OutputRoundedValues(dArr, MidpointRounding.AwayFromZero);
// Rounding with mr = MidpointRounding.AwayFromZero:
// Math.Round(12.345, 2, mr) => 12.35
// Math.Round(12.355, 2, mr) => 12.36
// decimal.Round(12.345, 2, mr) => 12.35
// decimal.Round(12.355, 2, mr) => 12.36
OutputToStringFormatted(dArr, "N2");
// decimal.ToString("N2"):
// 12.345.ToString("N2") => 12.35
// 12.355.ToString("N2") => 12.36
OutputToStringFormatted(dArr, "F2");
// decimal.ToString("F2"):
// 12.345.ToString("F2") => 12.35
// 12.355.ToString("F2") => 12.36
OutputToStringFormatted(dArr, "###.##");
// decimal.ToString("###.##"):
// 12.345.ToString("###.##") => 12.35
// 12.355.ToString("###.##") => 12.36
Console.ReadKey();
}
private static void OutputBaseValues(decimal[] dArr)
{
Console.WriteLine("Base values:");
for (int i = 0; i < dArr.Length; i++) Console.WriteLine("d[{0}] = {1}", i, dArr[i]);
Console.WriteLine();
}
private static void OutputRoundedValues(decimal[] dArr)
{
Console.WriteLine("Rounding with default MidpointRounding:");
foreach (decimal d in dArr) Console.WriteLine("Math.Round({0}, 2) => {1}", d, Math.Round(d, 2));
foreach (decimal d in dArr) Console.WriteLine("decimal.Round({0}, 2) => {1}", d, decimal.Round(d, 2));
Console.WriteLine();
}
private static void OutputRoundedValues(decimal[] dArr, MidpointRounding mr)
{
Console.WriteLine("Rounding with mr = MidpointRounding.{0}:", mr);
foreach (decimal d in dArr) Console.WriteLine("Math.Round({0}, 2, mr) => {1}", d, Math.Round(d, 2, mr));
foreach (decimal d in dArr) Console.WriteLine("decimal.Round({0}, 2, mr) => {1}", d, decimal.Round(d, 2, mr));
Console.WriteLine();
}
private static void OutputToStringFormatted(decimal[] dArr, string format)
{
Console.WriteLine("decimal.ToString(\"{0}\"):", format);
foreach (decimal d in dArr) Console.WriteLine("{0}.ToString(\"{1}\") => {2}", d, format, d.ToString(format));
Console.WriteLine();
}
The paragraph from section 4.1.7 of the C# Language Specification ("The decimal type") (get the full spec here (.doc)):
The result of an operation on values of type decimal is that which would result from calculating an exact result (preserving scale, as defined for each operator) and then rounding to fit the representation. Results are rounded to the nearest representable value, and, when a result is equally close to two representable values, to the value that has an even number in the least significant digit position (this is known as “banker’s rounding”). A zero result always has a sign of 0 and a scale of 0.
It's easy to see that they may not have been considering ToString(string) in this paragraph, but I'm inclined to think it fits in this description.
If you read the spec carefully, you will see that there is no inconsistency here.
Here's that paragraph again, with the important parts highlighted:
The result of an operation on values of type decimal is that which would result from calculating an exact result (preserving scale, as defined for each operator) and then rounding to fit the representation. Results are rounded to the nearest representable value, and, when a result is equally close to two representable values, to the value that has an even number in the least significant digit position (this is known as “banker’s rounding”). A zero result always has a sign of 0 and a scale of 0.
This part of the spec applies to arithmetic operations on decimal; string formatting is not one of those, and even if it were, it wouldn't matter because your examples are low-precision.
To demonstrate the behaviour referred to in the spec, use the following code:
Decimal d1 = 0.00000000000000000000000000090m;
Decimal d2 = 0.00000000000000000000000000110m;
// Prints: 0.0000000000000000000000000004 (rounds down)
Console.WriteLine(d1 / 2);
// Prints: 0.0000000000000000000000000006 (rounds up)
Console.WriteLine(d2 / 2);
That's all the spec is talking about. If the result of some calculation would exceed the precision limit of the decimal type (29 digits), banker's rounding is used to determine what the result will be.
ToString() by default formats according to the Culture, not according to a computational aspect of the specification. Apparently the Culture for your locale (and most, from the looks of it) expects rounding away from zero.
If you want different behavior, you can pass an IFormatProvider in to ToString()
I thought the above, but you are correct that it always rounds away from zero no matter the Culture.
As also linked by a comment on this answer, here (MS Docs) is official documentation on the behavior. Excerpting from the top of that linked page, and focusing on the last two list items:
Standard numeric format strings are used to format common numeric types. A standard numeric format string takes the form Axx, where:
A is a single alphabetic character called the format specifier. Any numeric format string that contains more than one alphabetic character, including white space, is interpreted as a custom numeric format string. For more information, see Custom Numeric Format Strings.
xx is an optional integer called the precision specifier. The precision specifier ranges from 0 to 99 and affects the number of digits in the result. Note that the precision specifier controls the number of digits in the string representation of a number. It does not round the number itself. To perform a rounding operation, use the Math.Ceiling, Math.Floor, or Math.Round method.
When precision specifier controls the number of fractional digits in the result string, the result string reflects a number that is rounded to a representable result nearest to the infinitely precise result. If there are two equally near representable results:
On the .NET Framework and .NET Core up to .NET Core 2.0, the runtime selects the result with the greater least significant digit (that is, using MidpointRounding.AwayFromZero).
On .NET Core 2.1 and later, the runtime selects the result with an even least significant digit (that is, using MidpointRounding.ToEven).
As far as your question ---
Is there a good reason this is the case? Or is this just an inconsistency in the language?
--- the answer implied by the change in behavior from Framework to Core 2.1+ is possibly, "No, there was no good reason, so we (Microsoft) went ahead and made the runtime consistent with the language in .NET Core 2.1 and later."
Most likely because this is the standard way of dealing with currency. The impetus for the creation of decimal was that floating point does a poor job of dealing with currency values, so you would expect it's rules to be more aligned with accounting standards than mathematical correctness.