• Jerry Kindall (unregistered)

    AppleScript (the Mac scripting language) uses banker's rounding (round-to-even). However, users complained that it wasn't getting the results they expected from their high school math classes, so in an update Apple added a new parameter: "rounding as taught in school."

    round 2.5 -> 2

    round 2.5 rounding as taught in school -> 3

  • jimbo (unregistered) in reply to John Cowan
    John Cowan:
    It's not possible to predict "the" name of a base-6 system, as the existing and well-established names reflect multiple Latin and Greek derivations.

    "Decimal" is from the Latin adjective for "a tenth part of", and if we had stuck to that, we'd have "dimidial" for base 2. Instead, we went with "binary", from the Latin for "in two parts". That would lead us to "octonary" for base 8, or if we stuck with the pattern of "decimal" we'd get "octonal", but no, we have "octal", apparently from a lame analogy with "decimal". "Ternary" is more often seen now for base 3, in strict analogy to "binary", but "trinary" is perfectly good Latin too.

    As for "hexadecimal", it's half Greek half Latin, and was probably imposed by higher-ups at IBM for the engineers' original "sexadecimal" (though "sextidecimal" would have been better Latin all around).

    So use "senary" or "sextenary", or "sexal" if you must, or replace the "s" with "h" in any of these if you feel you have to. Or do something else. Or say "base six" in plain English; it's short.

    basix. Loved the post.

  • MJ Fan (unregistered)

    Default VB Functionality:

    Ctrl-G (Immediate Window)

    ?Round(39.995,2) 39.99

    ?Round(11.995,2) 12

    Google:

    vb round 30 function

    URL:

    http://ewbi.blogs.com/develops/2003/11/vba_round_yet_a.html

    Quote:

    It's interesting to see how this happens. When 37.535 is passed through the expression CDbl(Number * (10 ^ NumDigitsAfterDecimal)), where NumDigitsAfterDecimal is 2, it does not return 3753.5. It returns 3753.4999999999994543 (that's the "inexactness" part). This value is increased by .5, becoming 3753.9999999999994543. This is then run through Fix, leaving 3753. Finally, it is divided by 100 to become 37.53. Which is definitely not what we're looking for.

  • me (unregistered) in reply to tuomas aaltio
    tuomas aaltio:
    it's a generally known fact that == operation fails in all programming languages, if the operands are of type float. == works only with integers.
    Actually, SML keeps you from using equality comparison on reals.
  • Scottford (unregistered) in reply to Derrick Pallas
    Derrick Pallas:
    But this is all because IEEE floating-point is a finite space. Real numbers are an infinite space. So, no, there are no numbers that are representable in IEEE floating-point that are not representable in decimal.

    Wow this is fascinating. Probably the first time I've actually learned something interesting (and not just hilarious) from tdwtf.

    But I'm not following all your explanations. Let me see if I can recap. Based on my understanding, I think the following statement is true:

    There is no binary floating point number (of any finite length) that is not exactly representable as a decimal number of finite length.

    Right?

  • TSK (unregistered) in reply to Scottford
    Scottford:
    Derrick Pallas:
    But this is all because IEEE floating-point is a finite space. Real numbers are an infinite space. So, no, there are no numbers that are representable in IEEE floating-point that are not representable in decimal.

    Wow this is fascinating. Probably the first time I've actually learned something interesting (and not just hilarious) from tdwtf.

    But I'm not following all your explanations. Let me see if I can recap. Based on my understanding, I think the following statement is true:

    There is no binary floating point number (of any finite length) that is not exactly representable as a decimal number of finite length.

    Right?

    Correct, but not for the explanation Derrick has given. If you use a number system with base three, you have still infinite many numbers in both systems, but there exists infinitely many finite binary floating point numbers which cannot be expressed by finite trinary floating point numbers. I don't know if I can explain it clearer than in my former post...?-|

  • Maciek (unregistered)

    I saw many people saying that Banker's rounding has nothing to do with the code presented.

    Well almost. Fact that function Round()in VB6 performs Banker's rounding MAY be the explanation of existence of that code. And that's probably what posters writing about Banker's rounding meant.

    And to someone asking about decimal type in VB6. Well it's a bit hidden but it exists.

    There is no Decimal declarator.

    Dim a as Decimal ' It's an error

    One has to use Varaint data type

    Dim a as Variant ' Or simply just Dim a Dim b as Variant Dim c as Variant

    a = CDec(2.3) b = CDec(4.6)

    c = a + b ' operation performed on decimal types

  • rankamateur (unregistered) in reply to TSK
    Correct, but not for the explanation Derrick has given. If you use a number system with base three, you have still infinite many numbers in both systems, but there exists infinitely many finite binary floating point numbers which *cannot* be expressed by finite trinary floating point numbers. I don't know if I can explain it clearer than in my former post...?-|
    Maybe I can.

    All real numbers are either rational or irrational.

    Irrational numbers can never be expressed as a finite series of digits in any (integer) base, because that would make them rational. So don't worry about those.

    Every rational number can (by definition) be expressed as a fraction. A fraction can be represented exactly as a sequence of digits in a certain base if and only if some power of that base is divisible by the denominator.* In other words: If all the prime factors of the denominator are also factors of the base.

    Since all the prime factors of 2 are also prime factors of 10, all numbers that can be represented precisely in binary can also be represented precisely in decimal. Since not all the prime factors of 10 are present in 2 (specifically, 5), the reverse is not true.

    Because neither 2 nor 10 is divisible by 3 (no matter what power you take them to), you can't express 1/3 in either binary or decimal. Similarly, there are lots of binary and decimal numbers you cannot write in base 3.

    *: Because a string of n digits a_1, a_2, ..., a_n after the . in some base b is the same as (a_1^n + a_2^(n-1) + ... + a_n)/b^n, and you can only have A/b^n = x/y if b^n is divisible by y (assuming x/y cannot be simplified).

  • anonymous (unregistered)

    The answer is in "It's Visual Basic --- an important fact --- so jot that down.". Is there a standard like ANSI/ISO for VB that lays down what each of the functions in this code are REQUIRED to return? Compare this situation to C/C++. ANSI/ISO C/C++ goes into gory details on what pow(), printf(), scanf() etc are required to do).

    VB has no documentation that confirms/denies what kind of arithmetic it uses (which version of VB?, is it IEEE754 etc). VB has no detailed documentation on INT() or any arithmetic operations involved here.

    In essence, the code is neither buggy nor bad. There is nothing to verify it against.

    I did not want to change the original VB code by more than necessary, so C version is also so long.

    Thanks.

    === here is C version and its output on Intel Box/WinXP/Cygwin, gcc-3.4.4 ===

    #include <stdio.h> #include <math.h>

    /* Public Function roundTo( number as Double, digits as Integer ) As Double Dim temp As Double Dim factor As Double

      factor = 10 ^ digits
      temp = number * factor + 0.5
      roundTo = Int(temp) / factor
    

    End Function */

    double vb_roundto(double number, int digits) {

        double temp;
        double factor;
        double res;
    
        factor =  pow(10.0, digits);
        temp = number * factor + 0.5;
        res = (int)temp / factor;
        return res;
    

    }

    int main() { double d;

        while (scanf("%lf", &d)) {
                printf("%lf\n", vb_roundto(d, 2));
    
        }
        return 0;
    

    }

    $ gcc -O3 vbroundc.c $ ./a.exe 3.9 3.900000 3.99 3.990000 3.999 4.000000 39.999 40.000000 39.999999 40.000000 3999.99999 4000.000000 4000.000000

  • (cs) in reply to TSK
    TSK:
    Scottford:
    But I'm not following all your explanations. Let me see if I can recap. Based on my understanding, I think the following statement is true:

    There is no binary floating point number (of any finite length) that is not exactly representable as a decimal number of finite length.

    Right?

    Correct, but not for the explanation Derrick has given. If you use a number system with base three, you have still infinite many numbers in both systems, but there exists infinitely many finite binary floating point numbers which cannot be expressed by finite trinary floating point numbers.

    You're correct: my last sentence didn't follow from the previous sentence, which was about the behavior of nextafter(...). There are infinitely more non-representable numbers than representable numbers in any base because between any two representable numbers of a given length there are infinitely many non-representable numbers.

    The last statement skips over the following sentence. Since two shares a non-trivial prime factor (namely itself) with ten, it is possible to represent all finite binary numbers in decimal. The talk about carnality is because I was comparing finite IEEE floating-point to the infinite real line, which means that finding non-representable numbers is very easy.

  • Dale (unregistered) in reply to Joeri
    Joeri:
    All decent programming languages have decimal arithmetic libraries (like the aforementioned BigDecimal) that you should use instead of floating points under one of these two scenario's.

    Worth noting that Ada has decimal numbers as a built in native type.

  • Phil (unregistered)

    In some piece of code I once found the following for converting counted bytes to megabytes.

    my $MB = ($BYTES / 1024 / 1024) + 1;

    This does not matter if it is run once a day, which sums up to 30 MBytes in, 30 MBytes out per month.

    If someone realizes that a 32bit counter can easily wrap around several times a day and changes the schedule from once a day to once an hour, you get a lot more:

    24h * 30d = 720 MB

    So, a totally unused line accumulates 1.44 GBytes of traffic per month of fictional usage. Good luck explaining this to the customer... Why not just record the bytes and do all the conversion-foo at the end of the month?

  • Tonyb103 (unregistered)

    Just two days ago I had a VB program adding two 2 decimal place numbers together and then failing a comparison that should have passed. Since these were amounts of money, I tried the Currency type and hey presto problem solved. I wish I had kept a note of the numbers, they were not very big at all.

    Captcha: ninjas -yeah!

  • (cs) in reply to Simon Bradley
    Simon Bradley:
    Real number: 39.995

    Representation is: SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM 0100 0010 0001 1111 1111 1010 1110 0001 = 421f fae1

    Sign is Positive Exponent is 132 biased is 5 Mantissa is 1.2498437166213989000000000000000000000000 Value = 1.2498437166213989000000000000000000000000 x 2^5 = 39.9949989318847660000000000000000000000000 = 39.9949989318847660000000000000000000000000

    So, yes, an IEEE 754 representation of 39.995 to 2DP is 39.99.

    Hooray for Simon .. super.

    From my own experience I know about the problems with floating point math. In one of the first projects I worked as a programmer our team had the benefit of a very sharp project manager who anticipated these kind of issues straightaway. One of the first libraries we wrote for us was on top of the floating point language type in ANSI-C (yes - that was waaaaay back but the issues have not changed - at all). The very first version of that library started out like this:

    <snip from lib header - start> #define EPS 0.001 #define GMEPS 0.00001 #define PI 3.14159265358979323846264 #define PI2 6.28318530717958647692528 <snip from lib header - end>

    <snip from lib source - start> int comp(a, b, epsilon) double a, b, epsilon; /* Compares two doubles by using the epsilon condition, whereas two doubles are considered equal if their difference is smaller than a given epsilon

    Returns 1 on "equality" of r1 and r2, -1 on a < b, 1 on a > b. */ { double r;

    r = fabs(a-b);
    if (r < epsilon)
    	return(0);
    else if (a > b)
    	return(1);
    else
    	return(-1);
    

    } <snip from lib source - end >

    it would have been called like this: if (comp(39.95, 39.9500000006, EPS) == 0)) { <yadayada> }

    You will notice that we had two different epsilons defined for different application modules in our project the subject of was the control for flame-cutting machines used for cutting steel plates for ship's hulls. The absolute number for the constants were spec'd by the project manager manager mentioned above who was a mechanical engineer and as such our subject matter expert.

  • Jimmy Jones (unregistered) in reply to tuomas aaltio
    tuomas aaltio:
    it's a generally known fact that == operation fails in all programming languages, if the operands are of type float. == works only with integers.

    Nope.

    "==" works perfectly well for floating point numbers.

    The amount of misconceptions and misinformation in this thread is astounding.

  • MrBester (unregistered) in reply to Maciek
    Maciek:
    <snip/>

    a = CDec(2.3) b = CDec(4.6)

    c = a + b ' operation performed on decimal types

    So how does VB do CDec(39.995) (which has been established to be misrepresented if it isn't declared as a BCD in the first place)? It looks at the value to pass in, notices a decimal point and thinks "ah, a Single!" (or possibly "ah, a Double!" depending on the number of decimal places and the integer size), converts it on the fly to the relevant type and then converts that floating point value (now 39.9949...) to a BCD. Isn't this where we came in? ;)

    Captcha: pirates. Arrr!

  • Eirik (unregistered)

    You should never ever use == with floating point numbers, every serious coder knows that. It because they are represented with a binary exponential. The conversion between binary and decimal very often lead to slight inaccuracies.

  • Maciek (unregistered) in reply to MrBester
    MrBester:
    So how does VB do CDec(39.995) (which has been established to be misrepresented if it isn't declared as a BCD in the first place)? It looks at the value to pass in, notices a decimal point and thinks "ah, a Single!" (or possibly "ah, a Double!" depending on the number of decimal places and the integer size), converts it on the fly to the relevant type and then converts that floating point value (now 39.9949...) to a BCD. Isn't this where we came in? ;)

    That was just an example to ilustrate use of Decimals.

    To perform computations with monetary values one can use currency data type and there is a currency literal in VB6 or can use Deciomal data type converting from currency literal CDec('currency literal here') and obtain exact results.

  • Mark (unregistered)

    This is simple, it's because floating point numbers aren't 100% accurate and int() actually floors the value.

    39.995+.005 = 39.9999999999999999 or something as a floating point. Floor this and you get 39.

    11.995+.005 = 12.000000000001 or something, which floors to 12.

    captch: waffles

  • XXX (unregistered) in reply to BBT
    BBT:
    $0.001 = 1/1000 of a cent, according to Verizon Wireless.

    Should that not be 1/10 of a cent?

  • Jimmy Jones (unregistered) in reply to Eirik
    Eirik:
    You should never ever use == with floating point numbers, every serious coder knows that.

    Wrong.

    float x = 1.23; float y = 1.23;

    if (x == y) { // This works perfectly... }

    It because they are represented with a binary exponential.

    Completely irrelevant.

    The conversion between binary and decimal very often lead to slight inaccuracies.

    Maybe the problem is that you're doing conversions where you shouldn't be.

  • 4am=tiredeyes (unregistered) in reply to vern
    vern:
    If we were using a number system that was say, 6-based instead of 10-based, we could show 1/3 with a finite number of digits after the '.' <-- we probably wouldn't call the '.' a decimal anymore either...

    In a number system with arbitrary base, the '.' is called the "radix point".

    • et
  • Fromage (unregistered) in reply to anonymous

    "i only know this because he was on futurama, which is not at all nerdy"

    If this comment isn't sarcastic, it is the biggest wtf of all!

  • (cs) in reply to John Cowan
    John Cowan:
    It's not possible to predict "the" name of a base-6 system, as the existing and well-established names reflect multiple Latin and Greek derivations.

    "Decimal" is from the Latin adjective for "a tenth part of", and if we had stuck to that, we'd have "dimidial" for base 2. Instead, we went with "binary", from the Latin for "in two parts". That would lead us to "octonary" for base 8, or if we stuck with the pattern of "decimal" we'd get "octonal", but no, we have "octal", apparently from a lame analogy with "decimal". "Ternary" is more often seen now for base 3, in strict analogy to "binary", but "trinary" is perfectly good Latin too.

    As for "hexadecimal", it's half Greek half Latin, and was probably imposed by higher-ups at IBM for the engineers' original "sexadecimal" (though "sextidecimal" would have been better Latin all around).

    So use "senary" or "sextenary", or "sexal" if you must, or replace the "s" with "h" in any of these if you feel you have to. Or do something else. Or say "base six" in plain English; it's short.

    They all sound a bit lame/awkward pronounciation for me. I vote we go with the name "sexual" derived from "sexal".

    Then we can all start using the sexual number system

  • Matthew (unregistered) in reply to anonymous
    anonymous:
    VB uses banker's rounding http://en.wikipedia.org/wiki/Banker%27s_rounding , a well understood WTF. There's even a Microsoft KB article, can't find the id at the moment.

    Banker's Rounding is completely irrelevant for 2 reasons.

    a) Int doesn't use it (CInt does). b) Banker's Rounding rounds to the even (not the odd), so if that was the problem it would round to 40.00.

  • Matthew (unregistered) in reply to aaron
    aaron:
    What went wrong is that he's programming in Visual Basic.

    He should re-write it in Pascal, now THERE'S a REAL language.

    (captcha: muhahaha -- diabolical?)

    The issue is floating point, not VB.

  • Matthew (unregistered) in reply to Slepnir
    Slepnir:
    Visual basic is like alcohol. When used responsibly, it's ok. When abused, bad things (like this guy) happen.

    temp = number * factor + 0.5

    should be

    return INT(number + (factor * 0.5))

    You obviously didn't test that code.

  • Matthew (unregistered) in reply to pipTheGeek
    pipTheGeek:
    What I don't get is why the Round() function is not used and why Decimal is not used for the type instead of Double
    If I remember rightly VB6 does not have a decimal type. You either used currency (fixed precision at 4 decimal places) or you use Double (the most accurate non integer type that vb6 supports) with all the already talked about oddness.

    VB 6 supports Decimal as a variant subtype. Its slow though.

    Check out CDec.

  • Matthew (unregistered) in reply to chmod755
    chmod755:
    In the Microsoft world, they call this "Midpoint Rounding". You have to tell the rounding mechanism what you want to do if the digit you're using to round is exactly in the middle. In this case, one could argue that you could either round down or round up.

    The Math class in the .Net framework provides a Round method, and most people normally use it with either one parameter (the number to be rounded) or two (the number of digits to round to). It can take a third, which is an enum called "MidpointRounding", and it's possible values are "AwayFromZero" or "ToEven". Here's a link to MSDN's documentation about Midpoint Rounding.

    A buddy of mine <a rel="nofollow" href="http://www.codedig.com/blog/2007/01/09/rounding-in-c/"" target="_blank" title="http://www.codedig.com/blog/2007/01/09/rounding-in-c/""> blogged about this a few days ago, and the posting has a code snippet to demonstrate usage (in C#).

    This has nothing to do with the issue at hand.

  • Matthew (unregistered) in reply to MrBester
    MrBester:
    Maciek:
    <snip/>

    a = CDec(2.3) b = CDec(4.6)

    c = a + b ' operation performed on decimal types

    So how does VB do CDec(39.995) (which has been established to be misrepresented if it isn't declared as a BCD in the first place)? It looks at the value to pass in, notices a decimal point and thinks "ah, a Single!" (or possibly "ah, a Double!" depending on the number of decimal places and the integer size), converts it on the fly to the relevant type and then converts that floating point value (now 39.9949...) to a BCD. Isn't this where we came in? ;)

    Captcha: pirates. Arrr!

    Wrap the number in quotes, or stick the currency identifier at the end - I think its @.

    CDec("39.995") CDec(39.995@)

  • Anonymous (unregistered)

    No one noticed the itoa/iota "joke"? Or no one thought it was worth commenting on?

  • SuzieQ (unregistered)

    I'm suprised that no one has mentioned an article that every programmer or computer-type should know by heart: "What every Computer Scientist Should Know About Floating Point Arithmetic" originally by David Goldberg. If you google the title it will find many copies all over the 'net.

    All of these things (and more) are explained and EVERYONE should be aware of this possibilities when they are designing or implementing code.

    Susan

  • Adam (unregistered)

    VB uses bankers rounding.

    You can use the VB format function to round properly ie Format(dValue, "0.00")

    There is actually many other different ways to round (about 20 the last time I looked).

  • Matthew (unregistered) in reply to Adam
    Adam:
    VB uses bankers rounding.

    You can use the VB format function to round properly ie Format(dValue, "0.00")

    There is actually many other different ways to round (about 20 the last time I looked).

    THIS HAS NOTHING TO DO WITH IT.

  • justme (unregistered) in reply to Marcin
    Marcin:
    UltimApe:
    0.333 != 1/3

    however, 0.333... (repeating ad infinitum) == 1/3

    going with thta. 1 != .999, but 1 == .999... (repeating ad infinitum)

    True, but I fail to see the relevance.
    I thought people here would know that "1 == .999... (repeating ad infinitum)" is not true.

  • Bob (unregistered) in reply to justme
    justme:
    Marcin:
    UltimApe:
    0.333 != 1/3

    however, 0.333... (repeating ad infinitum) == 1/3

    going with thta. 1 != .999, but 1 == .999... (repeating ad infinitum)

    True, but I fail to see the relevance.
    I thought people here would know that "1 == .999... (repeating ad infinitum)" is not true.

    Citation?

  • hpeg (unregistered) in reply to Bert

    Sorry, my bad, but the idea was there. Shouldn't do these things in a hurry...

    captcha: darwin, guess that fits

  • Rhialto (unregistered) in reply to AdT
    AdT:
    And although Simon Bradley came to the rescue and noted that 39.995 is actually represented as the binary equivalent of ca. 39.994999, I have no illusions that everyone who even took the trouble to read this comment understood the implications.
    Indeed - since the same problem would exist for the other number, since it has the same infinite expansion of the decimal part. (The only difference *may* be that the exponent is lower)
  • Georges (unregistered)

    Previous posters are spot-on that this stems from how floating point values are represented inside a computer.

    One poster was flamed for commenting that == doesn't work for floats/doubles. Well it's true and not true. == does work if the values are identical. The problem is that as a result of a computation we need to expect there can be small differences. Consider C# code:

       float x = 33.33F;
       float y = 11.11F;
    
       y = y * 3.0F;
    
       Assert.AreEqual(x, y); // this fails 
       Assert.AreEqual(x, y, .1); // this passes. numbers are equal within a delta of .1
    

    Trivially 11.11 * 3 = 33.33 , but as others have pointed out it's the inexact representations of a floating point number that leads to these kinds of oversight errors and to == not working as we expect it to.

    If you're interested go and read further: http://www.yoda.arachsys.com/csharp/floatingpoint.html

  • Rabiator (unregistered) in reply to Charles Perreault
    Charles Perreault:
    Cotillion:
    I think the problem is that he's mixing floats and doubles. I had a similar thing happen in C#. The 0.5 is instantiated as a float and then cast to a double. Sometimes, funny things can happen when casting between the two.

    In fact nothing funny can happen if you're using a IEEE754 cpu (like x86 or Sparc). Casting from a float to double only adds zero padding to the exposant and mantice for increased precision, no rounding whatsoever happens.

    Actually, the rounding behaviour of x86 processors is not fixed. It is controlled by a 16bit control word that is loaded into the FPU with the FLDCW command. See also http://www.website.masmforum.com/tutorials/fptute/fpuchap1.htm#cword

    In practice, we (the programmers at my place of work) have seen the behaviour depend on the computer model (drivers?) and the status of the network in Windows. The solution was to set the control word to a fixed value each time before we do a critical calculation, and put it back afterwards. So there is definitely another WTF with x86 and Windows.

  • Roger Wolff (unregistered)

    Everybody is talking about floating point numbers as being involved. They are, but not as much as the "base 2" that they work in.

    In decimal you have fractions like "1/2" and "1/5" that are exactly representable: 0.5 and 0.2.

    In decimal you also have fractions like "1/3" and "1/7" that repeat endlessly: 0.3333333... and 0.142857142857....

    In binary you have the same: 1/2 and 1/4 are finitely expresable, 1/3, 1/5, 1/10 are not. In binary they are: 0.01010101... 0.001100110011.... and 0.00011001100110011 respectively.

    What does this all mean? This means that when we write 2.5 this can be represented exactly in binary: 00010.10000000 . This translates to an exact representation in a floating point number.

    However, when we come to 39.995 this is a repeating fraction in binary: 100111.1111110101110000 (10100011110101110000)* , which, with fixed-length floating point numbers ends up rounded a bit.

    If you multiply it by 100 in any fixed-length binary format, you'll end up pretty close to 3999.5 (111110011111.1 in binary), but quite possibly also just a little short of that number. Adding .5, and rounding down to the nearest integer might just give you 3999.

    The only way to "fix" this is not to use fractions. If you are working with currency, you should do all the math in cents. Suppose your VAT percentage is 21. So to calculate the VAT on something priced 234.50, you do 0.21 * 234.50 = 49.2450. Multiply by 100, add 0.5 round down, and you get 4924, and not 4925.

    Work in cents, and you get the right results: multiply 23450 by 0.21 and you get 4924.5 cents (exact binary representation), which correctly rounds to 4925 cents.

    Moreover, using floats is dangerous too. At

  • CraigR (unregistered)

    As someone previously suggested using string manipulation, I was curious to see what it would take to minimally implement a rounding function in VB6 allowing for any arbitrary input with validation:

    Public Function RoundIt(ByVal Number As String, ByVal NumberOfDigits As Long) As String

    Dim char As String

    Dim lDecPos As Long Dim lOffset As Long

    Dim bNegative As Boolean Dim bStayNegative As Boolean Dim bFoundDecimal As Boolean Dim bDone As Boolean

    If NumberOfDigits < 0 Then Err.Raise 32000, , "Number of digits must be non-negative." End If

    Number = Trim$(Number) bNegative = Left$(Number, 1) = "-" If bNegative Then Number = Trim(Mid$(Number, 2)) End If

    If Number = "" Or Number = "." Then Err.Raise 32000, , "Not a valid decimal number." End If

    For lOffset = Len(Number) To 1 Step -1 char = Mid$(Number, lOffset, 1) Select Case char Case "." If bFoundDecimal Then Err.Raise 32000, , "Not a valid decimal number (multiple decimal points)." Else bFoundDecimal = True End If Case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" Case Else Err.Raise 32000, , "Not a valid decimal number." End Select Next

    lDecPos = InStr(Number, ".") If lDecPos = 0 Or Len(Number) - lDecPos <= NumberOfDigits Then bStayNegative = True Else char = Mid$(Number, lDecPos + NumberOfDigits + 1, 1) Number = Left$(Number, lDecPos + NumberOfDigits) If CInt(char) >= 5 Then For lOffset = Len(Number) To 1 Step -1 char = Mid$(Number, lOffset, 1) Select Case char Case "9" Mid$(Number, lOffset, 1) = "0" Case "0", "1", "2", "3", "4", "5", "6", "7", "8" Mid$(Number, lOffset, 1) = CStr(CInt(char) + 1) bStayNegative = True Exit For End Select Next Else For lOffset = Len(Number) To 1 Step -1 char = Mid$(Number, lOffset, 1) Select Case char Case "1", "2", "3", "4", "5", "6", "7", "8", "9" bStayNegative = True Exit For End Select Next End If End If

    Do While Left$(Number, 1) = "0" Number = Mid$(Number, 2) Loop

    If Number = "." Then RoundIt = "0." ElseIf bNegative And bStayNegative Then RoundIt = "-" & Number Else RoundIt = Number End If

    End Function

  • Bert (unregistered)

    Something in one of the posts made me think of this

    Salary Theorem The less you know, the more you make.

    Proof:

    Postulate 1: Knowledge is Power. Postulate 2: Time is Money. As every engineer knows: Power = Work / Time And since Knowledge = Power and Time = Money It is therefore true that Knowledge = Work / Money . Solving for Money, we get: Money = Work / Knowledge Thus, as Knowledge approaches zero, Money approaches infinity, regardless of the amount of Work done.

  • zompist (unregistered) in reply to justme
    justme:
    I thought people here would know that "1 == .999... (repeating ad infinitum)" is not true.

    Huh? Of course it is.

    1/3 = .3333.....

    Multiply both sides by 3

    1 = .9999.....

  • Matthew (unregistered) in reply to CraigR
    CraigR:
    As someone previously suggested using string manipulation, I was curious to see what it would take to minimally implement a rounding function in VB6 allowing for any arbitrary input with validation:

    This is completely irrelevant to the original posting. The issue is not with rounding.

  • justme (unregistered) in reply to zompist
    zompist:
    justme:
    I thought people here would know that "1 == .999... (repeating ad infinitum)" is not true.

    Huh? Of course it is.

    1/3 = .3333.....

    Multiply both sides by 3

    1 = .9999.....

    huh? no. You cannot multiply .9999~ by 3.

  • justme (unregistered) in reply to justme

    by ".9999~" I mean .3333~

  • Bezalel (unregistered)

    I haven't figured out why the fact that it is VB is so important. The only thing I could think of is the fact that Int() returns a Variant (I think of the same type as the parameter) and not an Integer but I don't know why that would matter.

  • darwin (unregistered) in reply to justme
    justme:
    huh? no. You cannot multiply .3333~ by 3.

    Of course you can. Why wouldn't you be able to multiply an infinite series by a number? It's just the equivalent of multiplying each term of the series by that number.

    And .9999... is equal to 1, of course, because it's just another representation for the series 9 / 10^i as i goes from 1 to infinity. Two different notations for the same thing.

  • darwin (unregistered) in reply to John Cowan
    John Cowan:
    It's not possible to predict "the" name of a base-6 system, as the existing and well-established names reflect multiple Latin and Greek derivations.

    "Decimal" is from the Latin adjective for "a tenth part of", and if we had stuck to that, we'd have "dimidial" for base 2. Instead, we went with "binary", from the Latin for "in two parts". That would lead us to "octonary" for base 8, or if we stuck with the pattern of "decimal" we'd get "octonal", but no, we have "octal", apparently from a lame analogy with "decimal". "Ternary" is more often seen now for base 3, in strict analogy to "binary", but "trinary" is perfectly good Latin too.

    As for "hexadecimal", it's half Greek half Latin, and was probably imposed by higher-ups at IBM for the engineers' original "sexadecimal" (though "sextidecimal" would have been better Latin all around).

    So use "senary" or "sextenary", or "sexal" if you must, or replace the "s" with "h" in any of these if you feel you have to. Or do something else. Or say "base six" in plain English; it's short.

    This is my absolute favorite comment ever on this site, and I've been reading the site on and off for several years now!

Leave a comment on “Round and Round”

Log In or post as a guest

Replying to comment #:

« Return to Article