Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Can this program be improved?

224 views
Skip to first unread message

Manu Raju

unread,
Dec 24, 2021, 10:38:05 PM12/24/21
to
>
>
> #include <stdio.h>
> #include <stdlib.h>
> #include <math.h>
>
> int main()
> {
>   float P = 100000; // Loan Amount
>   float r = 7.5; // Advertised Interest Rate
>   int n = 12; // Loan Period
>   float payBack = (P * r / 1200 * pow(1 + r / 1200, n)) / (pow((1 +
> r / 1200), n) - 1);
>   float Opening = P; // Opening Balance
>   float closing = 0;  // Closing Balance
>   float interest = 0;
>   float Total = 0;
>   float repaid = 0;
>
>   printf("\nMonthly payment is: %.2f\n", payBack);
>
>     printf("%10s %14s %13s %10s %14s %12s\n", "Month", "Beginning",
> "Interest", "Total", "Repayment", "Balance");
>
>     for (int i = 1; i <= n; i++)
>     {
>         for (int j = 0; j <= n; j++)
>         {
>             interest = Opening * r / 1200;
>             Total = Opening + interest;
>             repaid = payBack;
>             closing = Total - payBack;
>         }
>         printf("%10d %14.0f %13.0f %10.0f %14.0f %12.0f\n", i,
> Opening, interest, Total, repaid, closing);
>         Opening = closing;
>     }
>     return 0;
> }
>
> /*
> * Output should be:
> Monthly payment is: 8675.71
>      Month      Beginning      Interest      Total Repayment Balance
>          1         100000           625     100625 8676 91949
>          2          91949           575      92524 8676 83848
>          3          83848           524      84372 8676 75697
>          4          75697           473      76170 8676 67494
>          5          67494           422      67916 8676 59240
>          6          59240           370      59610 8676 50935
>          7          50935           318      51253 8676 42577
>          8          42577           266      42843 8676 34168
>          9          34168           214      34381 8676 25706
>         10          25706           161      25866 8676 17190
>         11          17190           107      17298 8676 8622
>         12           8622            54       8676 8676 0
>
> */
>
>
>
>
>


Andrey Tarasevich

unread,
Dec 25, 2021, 12:44:40 AM12/25/21
to
Certainly.

1. int main(void)

2. Stop using `float` for local variables. "Default" floating-point type
in C is `double`. Everything else is used only if you have a good reason
to do that. In this case you don't.

3. Stop using "regular" floating point types for representing monetary
quantities. They are not good for that purpose.

4. It appears that you are actually trying to use `float` to represent
_integer_ quantities (judging by your `printf`). Why?

5. Stop using `pow` for calculating integer powers. You don't really
need `<math.h>` here.

6. Stop piling up all variable declarations at the beginning of the
function. Declare variables as locally as possible.

7. Stop using dummy initializers for your variables. It is better to
leave them uninitialized than initialize them to dummy zeros. This point
is actually connected to the previous one: once you start declaring
variables as locally as possible, you'll normally have a meaningful
initializers for them at the point of declaration.

8. What's going on with capitalization in your variables names?
`Opening`, `closing`, `interest`, `Total`? Is this a convention of some
sort?

9. "Magical constants"? What on Earth is `1200`?

10. Repetitive subexpressions, like `1 + r / 1200` (some explicit, some
slightly obfuscated) are probably a matter of style.... But anyway: DRY
- do not repeat yourself.

--
Best regards,
Andrey Tarasevich

David Brown

unread,
Dec 25, 2021, 6:56:28 AM12/25/21
to
That's a good list. Let me add:

11. Be very suspicious of short variable names with a comment attached.
It /might/ be the best choice, because of the way the variable is used
or how it matches some external information (like a mathematical
formula). But often it is better to give the variable a more complete
name. (As a rule of thumb, the smaller the scope, the shorter the
variable name you can get away with.)

12. Be clear in your units - perhaps also including them in names or
even type information (though that is not common in C). Make it clear
which variables are money, percentages, monthly rates, annual rates, etc.

13. Any variables that are fixed at initialisation and not changed
should be declared with "const". It makes code clearer and lets the
compiler catch some kinds of mistakes.

14. It looks like you are mixing up 12 years loan period and 12 months
per year, using the same rather anonymous variable "n" for the purpose.


(Don't take this long list as a sign that your code is terrible. It's
not bad, and I'm sure we've all seen far worse. But these are all tips
for ways to improve it and get in good coding habits early.)

Bart

unread,
Dec 25, 2021, 7:48:06 AM12/25/21
to
Unlike the other replies, I think this program is largely OK. However
I'd make the following tweaks, for these reasons (I've assumed US$ amounts):

* Use double rather than float; this is more precise. With float, the
final balance will be out by some cents (hidden if shown to nearest
dollar, but sometimes you will see "-0" because of negative cents)

* I've made capitalisation more consistent

* I've lined up the comments, and added extra notes

* 'r' has been changed to 0.075 from 7.5%, to avoid that mysterious 1200
in the code. Now with '12' is easier to guess it is 12 months and you
are calculating a monthly rate.

* The display layout has been adjusted (see below), but that was just my
own preference.

On the last point, in your post, the last two columns were not properly
aligned, but when I tried, they were properly right-justified.

(Interestingly, the complicated expression initialising 'payback'
revealed a bug in one compiler, making it generate an erroneous value.
But that is not your problem. Just be aware if using Tiny C.)


Example:
------------------------------------------------------------

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

int main(void)
{
double P = 100000; // Loan Amount (all amounts dollars(etc))
double r = 7.5/100; // Advertised Interest Rate (percent/100,
annual)
int n = 12; // Loan Period (months)
double payback = // Monthly repayment
(P * r/12 * pow(1+r/12, n)) / (pow((1+r/12),n) - 1);
double opening = P; // Opening Balance
double closing = 0; // Closing Balance
double interest = 0;
double total = 0;
double repaid = 0;

printf("\nMonthly payment is: %.2f\n", payback);

printf("%10s %14s %10s %10s %10s %12s\n", "Month", "Beginning",
"Interest", "Total", "Repayment", "Balance");
puts("
------------------------------------------------------------------");

for (int i = 1; i <= n; i++)
{
for (int j = 0; j <= n; j++)
{
interest = opening * r / 12;
total = opening + interest;
repaid = payback;
closing = total - payback;
}
printf("%10d %14.0f %10.0f %10.0f %10.0f %12.0f\n", i,
opening, interest, total, repaid, closing);
opening = closing;
}
}

Lew Pitcher

unread,
Dec 25, 2021, 12:38:17 PM12/25/21
to
You've received very good advice from others. I'll add my voice
to the chorus, now. But, for only one point ...

On Sat, 25 Dec 2021 01:00:00 +0000, Manu Raju wrote:

>> #include <stdio.h>
>> #include <stdlib.h>
>> #include <math.h>
>>
>> int main()
>> {
>>   float P = 100000; // Loan Amount
>>   float r = 7.5; // Advertised Interest Rate
>>   int n = 12; // Loan Period
>>   float payBack = (P * r / 1200 * pow(1 + r / 1200, n)) / (pow((1 +
>> r / 1200), n) - 1);
>>   float Opening = P; // Opening Balance
>>   float closing = 0;  // Closing Balance
>>   float interest = 0;
>>   float Total = 0;
>>   float repaid = 0;
>>
>>   printf("\nMonthly payment is: %.2f\n", payBack);
>>
>>     printf("%10s %14s %13s %10s %14s %12s\n", "Month", "Beginning",
>> "Interest", "Total", "Repayment", "Balance");
>>
>>     for (int i = 1; i <= n; i++)
>>     {
>>         for (int j = 0; j <= n; j++)
>>         {
>>             interest = Opening * r / 1200;
>>             Total = Opening + interest;
>>             repaid = payBack;
>>             closing = Total - payBack;
>>         }

When writing a loop such as above, you should consider placing
loop-invariant calculations outside of the loop. Most
compilers would optimize the loop-invariant statements such that
they only execute once, so this isn't really a performance issue.

However, it /is/ a readability issue. So, your given logic would
be easier to read as...

for (int i = 1; i <= n; i++)
  {
   interest = Opening * r / 1200; /* neither Opening nor r change within the j loop */
    repaid = payBack; /* payBack does not change within the j loop */
for (int j = 0; j <= n; j++)
    {
     Total = Opening + interest;
      closing = Total - payBack;
    }
printf("%10d %14.0f %13.0f %10.0f %14.0f %12.0f\n",
i, Opening, interest, Total, repaid, closing);
Opening = closing;
}

>>         printf("%10d %14.0f %13.0f %10.0f %14.0f %12.0f\n", i,
>> Opening, interest, Total, repaid, closing);
>>         Opening = closing;
>>     }
>>     return 0;
>> }


--
Lew Pitcher
"In Skills, We Trust"

Öö Tiib

unread,
Dec 25, 2021, 1:58:26 PM12/25/21
to
On Saturday, 25 December 2021 at 19:38:17 UTC+2, Lew Pitcher wrote:
> interest = Opening * r / 1200; /* neither Opening nor r change within the j loop */
> repaid = payBack; /* payBack does not change within the j loop */

And since you removed "interest" out of j loop also "Total" should go out
and since "Total" went out also "closing" so go out and since j loop is
now empty it should be erased. ;-)

Tim Rentsch

unread,
Dec 25, 2021, 2:14:05 PM12/25/21
to
Andrey Tarasevich <andreyta...@hotmail.com> writes:

> On 12/24/2021 5:00 PM, Manu Raju wrote:
>
>> [revised to repair white space]
Check.

> 2. Stop using `float` for local variables. "Default" floating-point
> type in C is `double`. Everything else is used only if you have a
> good reason to do that. In this case you don't.

Check.

> 3. Stop using "regular" floating point types for representing
> monetary quantities. They are not good for that purpose.

For this level of programming I think floating point is okay. A
problem that is important is the program makes no effort to round
(to the nearest cent, for example), so the calculation gives
funny results typical of calculations done with floating point.
But that problem can be addressed without throwing out floating
point altogether.

> 4. It appears that you are actually trying to use `float` to
> represent _integer_ quantities (judging by your `printf`). Why?

To me that just looks like what the print formats are, not what
the kinds of quantities are.

> 5. Stop using `pow` for calculating integer powers. You don't
> really need `<math.h>` here.

I have to vote against this comment. Using pow() makes it
obvious what is being done, the exponent being integral
notwithstanding.

> 6. Stop piling up all variable declarations at the beginning of
> the function. Declare variables as locally as possible.

Good advice here I would say, but this rule is a point of style
with differing viewpoints.

> 7. Stop using dummy initializers for your variables. It is better
> to leave them uninitialized than initialize them to dummy zeros.
> This point is actually connected to the previous one: once you
> start declaring variables as locally as possible, you'll normally
> have a meaningful initializers for them at the point of declaration.

Check.

> 8. What's going on with capitalization in your variables names?
> Opening`, `closing`, `interest`, `Total`? Is this a convention of
> some sort?

Check.

> 9. "Magical constants"? What on Earth is `1200`?

Check. However I think it should be added that the "magicness"
of the constant(s) can be addressed by rephrasing the expressions
where they are used, without necessarily taking the constants
out of those expressions.

> 10. Repetitive subexpressions, like `1 + r / 1200` (some explicit,
> some slightly obfuscated) are probably a matter of style.... But
> anyway: DRY - do not repeat yourself.

More significant here, I think, is that defining a variable with
the value of this expression offers an opportunity to give some
sort of descriptive name to the quantity.


Two additional points:

The #include <stdlib.h> can be removed.

The inner loop (with 'j' as the index variable) does the same
calculation over and over again. There is no reason to make
that a loop - just write the body.

Lew Pitcher

unread,
Dec 25, 2021, 2:16:59 PM12/25/21
to
Why, yes. Now that you mention it.

But we must leave /some/ details as exercises for the student, agreed?

Tim Rentsch

unread,
Dec 25, 2021, 2:22:44 PM12/25/21
to
Manu Raju <M...@invalid.invalid> writes:

[revised to repair spacing issues]
I gave some other comments in another response. Here is
a re-writing to make my suggestions more concrete.

(Note to all: Merry Christmas!)


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

static void show_payment_table( double, double, int );

int
main(void){
show_payment_table( 100000, 7.5, 12 );
return 0;
}

void
show_payment_table( double amount, double yearly_percentage_rate, int months ){
double const monthly_rate = yearly_percentage_rate / 100 / 12;
double const compounded = pow( 1 + monthly_rate, months );
double const adjustment = compounded / (compounded - 1);
double const payment = amount * monthly_rate * adjustment;

double balance = amount;

printf( "\nMonthly payment is: %.2f\n", payment );

printf( "%10s %14s %13s %10s %14s %12s\n",
"Month", "Beginning", "Interest", "Total", "Repayment", "Balance"
);

for( int i = 1; i <= months; i++ ){
double const interest = balance * monthly_rate;
double const owing = balance + interest;
double const remaining = owing - payment;

printf( "%10d %14.0f %13.0f %10.0f %14.0f %12.0f\n",
i, balance, interest, owing, payment, remaining
);
balance = remaining;
}
}

Öö Tiib

unread,
Dec 25, 2021, 2:25:48 PM12/25/21
to
Oops. I added too much spoilers?
My bad. But usually at that point the teacher hints that
finding the rest of cases is exercise.

Scott Lurndal

unread,
Dec 25, 2021, 2:30:30 PM12/25/21
to
r...@zedat.fu-berlin.de (Stefan Ram) writes:
>Lew Pitcher <lew.p...@digitalfreehold.ca> writes:
>>You've received very good advice from others. I'll add my voice
>>to the chorus, now. But, for only one point ...
>
> I have a totally different POV on this than yall!
>
> 1st The "greater-than" signs at the start of every
> line are a violation of Usenet customs and/or
> syntax errors in the C program.

Or, perhaps, they are standard Usenet quoting characters, in use
for four decades.

Andrey Tarasevich

unread,
Dec 25, 2021, 2:53:21 PM12/25/21
to
On 12/25/2021 9:38 AM, Lew Pitcher wrote:
>
>    interest = Opening * r / 1200; /* neither Opening nor r change within the j loop */
>     repaid = payBack; /* payBack does not change within the j loop */
>

Yes, I missed this part. The whole loop

for (int j = 0; j <= n; j++)
{
interest = Opening * r / 1200;
Total = Opening + interest;
repaid = payBack;
closing = Total - payBack;
}

does not have any observable behavior, its body does not depend on `j`
and actions performed inside do not have any cumulative effect. Each
iteration begins in the same input state.

This means that the loop is completely pointless. It is equivalent to a
one-time execution of its body

interest = Opening * r / 1200;
Total = Opening + interest;
repaid = payBack;
closing = Total - payBack;

Malcolm McLean

unread,
Dec 25, 2021, 4:27:14 PM12/25/21
to
This line looks supicious. You are applying the interest 13 times. That might be
right (13 4 week months in a year), but it needs a comment.
YOu shouldn't re-use "n", as other have suggested.

Manfred

unread,
Dec 25, 2021, 5:50:38 PM12/25/21
to
On 12/25/2021 6:44 AM, Andrey Tarasevich wrote:
> 6. Stop piling up all variable declarations at the beginning of the
> function. Declare variables as locally as possible.

Put this way, this is questionable advice, IMO.
Experienced programmers may have your same opinion, but some others,
equally experienced, may label that same programming style as sloppy -
I've seen some myself.

In order to turn this opinion into good advice, I think there is more to
add to it:

1) Functions should perform simple, well defined tasks, which implies
that complex tasks should be split into multiple functions. After you do
this, then it becomes natural that you don't have "piles" of local
variables, and you may well have the variables you need at the beginning
of the function and still be "local".

2) Make good use of language constructs to keep counters, indexes etc.
local: for example, instead of declaring a generic counter "n" at the
beginning of the function and reuse it multiple times, prefer the following:

for (int n = 0; n < COUNT; ++n)
{
/* ... */
}

and thus have multiple "for" (and friends) constructs each with its own
counter.

3) If in the function body you have a code section that performs some
specific operation that needs its local variables, you may use a local
scope for it, and declare its variables inside it (check point 1 above
as well):

int myfunction(void)
{
/* ... */

{
int myvar = 42;
/* ... */
}

/* ... */
}

Obviously, the body of language constructs like "if", "for", "while",
"do" make natural local scopes that are good candidates for this purpose.

4) For algorithmic parameters that may affect the entire task, (e.g.
number of months in a year), you may well have a "const" variable at the
beginning of the function. "static const" at the beginning of the source
file is also an option, as well as many others.

>
> 7. Stop using dummy initializers for your variables. It is better to
> leave them uninitialized than initialize them to dummy zeros. This point
> is actually connected to the previous one: once you start declaring
> variables as locally as possible, you'll normally have a meaningful
> initializers for them at the point of declaration.

This is tricky. One of the worst bugs I have ever encountered was
"strlen" being called on an uninitialized string buffer.

I believe the best option is to initialize variables with meaningful
values, which leads to locality, as you say, but with my notes above.
However, as a last resort, it is probably better to leave them
uninitialized, rather than dummy zeros, *if* you have a decent compiler
that will spot its use before assignment.

Chris M. Thomasson

unread,
Dec 25, 2021, 6:01:58 PM12/25/21
to
Tell that to the bank... ;^)

Lew Pitcher

unread,
Dec 25, 2021, 6:09:43 PM12/25/21
to
Öö Tiib wasn't wrong. I didn't take my advice far enough :-)

As for "the bank", the OP's program produces identical results with
the removal of the j loop, so if the original program was good enough,
then the OP can take the loop-invariant version "to the bank" as well ;-)

Keep well, my friends.

Keith Thompson

unread,
Dec 25, 2021, 6:50:32 PM12/25/21
to
They are, but they're generally used to quote text from previous
articles. The code in the question is original text. I'm not
necessarily criticizing the original poster, but IMHO it would have been
better for the code not to be prefixed with anything, particularly so
that readers can copy-and-paste or save the article and easily extract
the code from it.

My newsreader (Gnus) showed me a lot of strange characters. Here's what
the original article looked like to me:

> #include <stdio.h>
> #include <stdlib.h>
> #include <math.h>
>
> int main()
> {
> \240 float P = 100000; // Loan Amount
> \240 float r = 7.5; // Advertised Interest Rate
> \240 int n = 12; // Loan Period
> \240 float payBack = (P * r / 1200 * pow(1 + r / 1200, n)) / (pow((1 +
> r / 1200), n) - 1);

the \240 is a NO-BREAK SPACE character. And when I tried to save the
article, emacs gave me an error message; it was unable to encode the
\240 characters to UTF-8. It appears that the original article was
encoded in ISO-8859-1 or something similar, but not marked as such in
the headers.

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */

Andrey Tarasevich

unread,
Dec 25, 2021, 7:06:57 PM12/25/21
to
On 12/25/2021 1:27 PM, Malcolm McLean wrote:
>
> for (int j = 0; j <= n; j++)
>
> This line looks supicious. You are applying the interest 13 times. That might be
> right (13 4 week months in a year), but it needs a comment.
> YOu shouldn't re-use "n", as other have suggested.

True, but as it has already been stated above, the body of the loop does
not have any cumulative effect. It is not really "applying" anything to
anything.

You can run this loop 10000 times, or you can run it only once - makes
no difference whatsoever.

Chris M. Thomasson

unread,
Dec 25, 2021, 11:56:42 PM12/25/21
to
Ahhhh! Touche. Merry Christmas!

Manu Raju

unread,
Dec 26, 2021, 1:59:32 PM12/26/21
to
> 9. "Magical constants"? What on Earth is `1200`?


It is just a figure 12 * 100. You first need to convert percentage into
decimals by dividing by 100; Then you need to divide the number again by
12 to give monthly rate. Interest rate is normally annual rate. Hope
this explains the magic number.





X-Mozilla-Status: 0811
X-Mozilla-Status2: 00000000
X-Mozilla-Keys:

Manu Raju

unread,
Dec 26, 2021, 1:59:32 PM12/26/21
to
Thank you for your feedback.

Kind regards Merry Christmas.

Manu Raju

unread,
Dec 26, 2021, 2:05:05 PM12/26/21
to
On 25/12/2021 17:38, Lew Pitcher wrote:
> You've received very good advice from others. I'll add my voice
> to the chorus, now. But, for only one point ...
>
>
>
> When writing a loop such as above, you should consider placing
> loop-invariant calculations outside of the loop. Most
> compilers would optimize the loop-invariant statements such that
> they only execute once, so this isn't really a performance issue.
>
> However, it /is/ a readability issue. So, your given logic would
> be easier to read as...
>
>


Thank you.


Manu Raju

unread,
Dec 26, 2021, 2:09:32 PM12/26/21
to
On 25/12/2021 19:22, Tim Rentsch wrote:
> Manu Raju <M...@invalid.invalid> writes:
>
> [revised to repair spacing issues]
>

Thank you. Very helpful and using a function was exactly what I wanted
to do.

Manu Raju

unread,
Dec 26, 2021, 2:20:45 PM12/26/21
to
On 25/12/2021 23:50, Keith Thompson wrote:
>
>
> My newsreader (Gnus) showed me a lot of strange characters. Here's what
> the original article looked like to me:

The code was copy/pasted from VS Code so that might be the reason.


>
>
> the \240 is a NO-BREAK SPACE character. And when I tried to save the
> article, emacs gave me an error message; it was unable to encode the
> \240 characters to UTF-8. It appears that the original article was
> encoded in ISO-8859-1 or something similar, but not marked as such in
> the headers.

The code was copied from VS Code and pasted in Mozilla Thunderbird. Not
sure why it changed.


>


Lew Pitcher

unread,
Dec 26, 2021, 2:22:34 PM12/26/21
to
Well, you've explained it to us, but not to whomever will read your program next.

A good part of the art of programming is writing readable code; readable to you,
readable to you in 5 years from now, readable to the programmer that has to make
the next change to it, readable to the programmer who has to fix the bug in it.

Your use of 1200 is confusing, unless you already know that it facilitates the
conversion of a yearly interest rate expressed as a percent into a monthly
interest rate expressed as a decimal. It is worth noting that, should you
change the loan period expressed in P, you would /also/ have to change this
1200 "magic number" appropriately (or is P a "loan period in years"? if so,
then I believe that your interest calculations are wrong.)

Manu Raju

unread,
Dec 26, 2021, 4:40:02 PM12/26/21
to
On 26/12/2021 19:22, Lew Pitcher wrote:
>
>
> Your use of 1200 is confusing, unless you already know that it facilitates the
> conversion of a yearly interest rate expressed as a percent into a monthly
> interest rate expressed as a decimal. It is worth noting that, should you
> change the loan period expressed in P, you would /also/ have to change this
> 1200 "magic number" appropriately (or is P a "loan period in years"? if so,
> then I believe that your interest calculations are wrong.)
>
>

The figure of 1200 will NEVER change. There will always be 12 months in
a year and we will always have to divide by 100 to express percentage as
decimals.

The interest rate can change and period (I used n) can also change. I used (n = 12) in my program to fit the table for people to see but
you can have 360 (30 years * 12). The program will still work.

Tim did a very good job to write a function. Did you see it? It is here.


<===========================================================================================>

void show_payment_table(double amount, double yearly_percentage_rate,
int months)
{
double const monthly_rate = yearly_percentage_rate / 100 / 12;
double const compounded = pow(1 + monthly_rate, months);
double const adjustment = compounded / (compounded - 1);
double const payment = amount * monthly_rate * adjustment;

double balance = amount;

printf("\nMonthly payment is: %.2f\n", payment);

printf("%10s %14s %13s %10s %14s %12s\n",
"Month", "Beginning", "Interest", "Total", "Repayment",
"Balance");

for (int i = 1; i <= months; i++)
{
double const interest = balance * monthly_rate;
double const owing = balance + interest;
double const remaining = owing - payment;

printf("%10d %14.2f %13.2f %10.2f %14.2f %12.2f\n",
i, balance, interest, owing, payment, remaining);
balance = remaining;
}

<===========================================================================================>

Now in the main function you just need to use it like so:

<===========================================================================================>

show_payment_table(250000, 5.0, 360);

<===========================================================================================>

This program is part of a big project where users will insert their
figures to calculate their repayments. all they need to enter will be
the Loan Amount, Interest Rate, and the Duration of the Loan. the
program will give them the schedule they can base their decision on. The
Windows Form/WPF will be used for these entries and "ASP.Net/MVC" for
the web site.

Best regards and have a very Happy New Year.



Bart

unread,
Dec 26, 2021, 5:03:04 PM12/26/21
to
On 26/12/2021 21:30, Manu Raju wrote:
> On 26/12/2021 19:22, Lew Pitcher wrote:
>>
>>
>> Your use of 1200 is confusing, unless you already know that it facilitates the
>> conversion of a yearly interest rate expressed as a percent into a monthly
>> interest rate expressed as a decimal. It is worth noting that, should you
>> change the loan period expressed in P, you would /also/ have to change this
>> 1200 "magic number" appropriately (or is P a "loan period in years"? if so,
>> then I believe that your interest calculations are wrong.)
>>
>>
>
> The figure of 1200 will NEVER change. There will always be 12 months in
> a year and we will always have to divide by 100 to express percentage as
> decimals.

Imagine this is part of a bigger program, and the constant 1200 also
occurs elsewhere with a different meaning.

/That/ 1200 might need to change, but now you don't know which 1200s are
relevant.

Richard Damon

unread,
Dec 26, 2021, 6:24:22 PM12/26/21
to
On 12/26/21 4:30 PM, Manu Raju wrote:
> The figure of 1200 will NEVER change. There will always be 12 months in
> a year and we will always have to divide by 100 to express percentage as
> decimals.

Actually that is a BIG assumption, that assumes that the ONLY payment
schedule is monthly.

Some people take out loans with Semi-Monthly (or B-Weekly) payment
schedules to pay things off a bit faster it that is how they get paid.

You might even choose weekly, or some are paid annually or things in
between.

All these change the 1200 to something else, to something like 100 *
payments_per_year.

Also, due to how common other operations on interest rates are, it may
make more sense on a program scale to do the rate/100 early in the
program and internally store these rates as the actual rate in per unit,
instead of the artificail percent (that would also allow parts of the
code to use permill if that was desired (per mill is per 1000 instead of
percent which is per 100).

Lew Pitcher

unread,
Dec 26, 2021, 7:47:42 PM12/26/21
to
On Sun, 26 Dec 2021 21:30:00 +0000, Manu Raju wrote:

> On 26/12/2021 19:22, Lew Pitcher wrote:
>>
>>
>> Your use of 1200 is confusing, unless you already know that it facilitates the
>> conversion of a yearly interest rate expressed as a percent into a monthly
>> interest rate expressed as a decimal. It is worth noting that, should you
>> change the loan period expressed in P, you would /also/ have to change this
>> 1200 "magic number" appropriately (or is P a "loan period in years"? if so,
>> then I believe that your interest calculations are wrong.)
>>
>>
>
> The figure of 1200 will NEVER change. There will always be 12 months in
> a year and we will always have to divide by 100 to express percentage as
> decimals.

30 years ago, when I bought my house, I took out a mortgage. The bank offered
me options to pay in monthly installments, or bi-weekly installments. The bi-weekly
installment option offered a lower individual payment, but at a higher frequency.

So, /yes/, the number of installments per cycle /may/ change.

As for 1200 being an appropriate figure, I disagree. Your 1200 represents /two/
distinct conversion factors. Speaking from a purely "design" point of view, you
never want constants to represent more than one thing.

In this case, your 1200 represents
a) the conversion factor that alters values expressed as a percent into values
expressed as a decimal, and
b) the conversion factor that alters values expressed as a year into values
expressed as a number of months.

While it may be convenient for /you/ to consolidate those two conversion factors
into one constant, that consolidation /is not/ obvious, and /can/ lead to issues
if one or the other of those two conversions change.

Remember, you are writing code to be /readable/. The computer doesn't care; it
doesn't know C from COBOL or Brainfuck: it only knows the ones and zeros that
represent it's internal language. What you write must be readable and comprehensible
to other programmers.

[snip]

Lew Pitcher

unread,
Dec 29, 2021, 10:22:02 AM12/29/21
to
One last thought....

On Sat, 25 Dec 2021 01:00:00 +0000, Manu Raju wrote:

[snip]
>>   float payBack = (P * r / 1200 * pow(1 + r / 1200, n)) / (pow((1 + r / 1200), n) - 1);
[snip]

You need some documentation, especially around the important but obscure
parts like the statement above.

While I've been out of the financial computing world for quite some time,
I still can recognize /parts/ of this statement as relating to compound
interest calculations, but the /exact/ calculation escapes me.

A comment within your code, describing the sort of calculation you make
here, along with a reference to it's source, would go a long way to
making the program readable.

When you come back to this code next year, or when your teacher
evaluates the code, such a comment would make the intent of this
calculation clearer (and perhaps even assist in debugging).

HTH

Guillaume

unread,
Dec 29, 2021, 11:53:13 AM12/29/21
to
Le 25/12/2021 à 06:44, Andrey Tarasevich a écrit :
> On 12/24/2021 5:00 PM, Manu Raju wrote:
>>>
>>> #include <stdio.h>
>>> #include <stdlib.h>
>>> #include <math.h>
>>>
>>> int main()
>>> {
>>>    float P = 100000; // Loan Amount
>>>    float r = 7.5; // Advertised Interest Rate
>>>    int n = 12; // Loan Period
>>>    float payBack = (P * r / 1200 * pow(1 + r / 1200, n)) / (pow((1 +
>>> r / 1200), n) - 1);
>>>    float Opening = P; // Opening Balance
>>>    float closing = 0;  // Closing Balance
>>>    float interest = 0;
>>>    float Total = 0;
>>>    float repaid = 0;
>>>
>>>    printf("\nMonthly payment is: %.2f\n", payBack);
>>>
>>>      printf("%10s %14s %13s %10s %14s %12s\n", "Month", "Beginning",
>>> "Interest", "Total", "Repayment", "Balance");
>>>
>>>      for (int i = 1; i <= n; i++)
>>>      {
>>>          for (int j = 0; j <= n; j++)
>>>          {
>>>              interest = Opening * r / 1200;
>>>              Total = Opening + interest;
>>>              repaid = payBack;
>>>              closing = Total - payBack;
>>>          }
>>>          printf("%10d %14.0f %13.0f %10.0f %14.0f %12.0f\n", i,
>>> Opening, interest, Total, repaid, closing);
>>>          Opening = closing;
>>>      }
>>>      return 0;
>>> }
>
> Certainly.
>
> 1. int main(void)
>
> 2. Stop using `float` for local variables. "Default" floating-point type
> in C is `double`. Everything else is used only if you have a good reason
> to do that. In this case you don't.
>
> 3. Stop using "regular" floating point types for representing monetary
> quantities. They are not good for that purpose.
>
> 4. It appears that you are actually trying to use `float` to represent
> _integer_ quantities (judging by your `printf`). Why?
>
> 5. Stop using `pow` for calculating integer powers. You don't really
> need `<math.h>` here.
>
> 6. Stop piling up all variable declarations at the beginning of the
> function. Declare variables as locally as possible.
>
> 7. Stop using dummy initializers for your variables. It is better to
> leave them uninitialized than initialize them to dummy zeros. This point
> is actually connected to the previous one: once you start declaring
> variables as locally as possible, you'll normally have a meaningful
> initializers for them at the point of declaration.
>
> 8. What's going on with capitalization in your variables names?
> `Opening`, `closing`, `interest`, `Total`? Is this a convention of some
> sort?
>
> 9. "Magical constants"? What on Earth is `1200`?
>
> 10. Repetitive subexpressions, like `1 + r / 1200` (some explicit, some
> slightly obfuscated) are probably a matter of style.... But anyway: DRY
> - do not repeat yourself.

All good points!

Bart

unread,
Dec 29, 2021, 12:49:14 PM12/29/21
to
All?

2) Nothing wrong with using 'float' when it's appropropriate. Many
libraries eg. OpenGL make extensive use of float.

It's not obvious that C's /default/ float type is 'double', nor that
'float' is not the default float type, despite the name!

However, for this application, float is not quite accurate enough.

3) Double is perfectly suitable for representing monetary amounts,
especially in a toy program or for personal use. (I've used it for
accounts within a small business.)

But what exactly would be a practical alternative here? To import a
decimal float library, or somehow work with int64 whole cents? (Good
luck with interest calculations with that.)

4) Doesn't make sense. The quantities have to be floating point, but are
simply being printed with zero decimal places, as whole dollars or whatever.

5) I can't see an issue in using pow() to do pow(float,int); the result
will not be exact anyway. What is the alternate, to invent a local
powint(float,int) routine? Which will probably contain a loop. Or change
the logic for an accumulative product. pow() is simpler!

6) I just don't agree with this at all. I like all the cluttery
declarations out of the way of the logic, and in one place for quick
reference. Then you also know there are no multiple, incompatible
instances of any local identifier.

7) I think initialisers are a good idea. Some languages will ensure such
locals are set to a known starting value, like 0 or 0.0 for static
types; presumably they think that is a benefit.

Ones like (1) I agree with, but I also think it's a waste of time
pointing it out, as the practice is so widespread.

Because compilers [not mine!] have long routinely accepted () parameter
lists without any warnings that args passed to those functions are
completely unchecked for number or types, most seem to assume that they
mean /no/ parameters.

In this example program, it means that the code could call main(10,
20.0, "three") without comment. But then it could still call it legally
as main() if the (void) was added.

Andrey Tarasevich

unread,
Dec 29, 2021, 2:34:04 PM12/29/21
to
> 2) Nothing wrong with using 'float' when it's appropropriate. Many
> libraries eg. OpenGL make extensive use of float.

That's just emphasizes my the point. The main purpose of "small" types,
like `float`, `short`, char as plain integer, bit-fields etc. is to save
memory in quantitatively massive data structures.

That's exactly what OpenGL is intended to work with: massive amounts of
geometric data. That's why it opts for `float` Everything else is just a
consequence of that.

> It's not obvious that C's /default/ float type is 'double', nor that
> 'float' is not the default float type, despite the name!

It doesn't have to be obvious. That's why I state it explicitly.

> 6) I just don't agree with this at all. I like all the cluttery
> declarations out of the way of the logic,

Declarations are never cluttery, unless you make a deliberate effort to
make them that.

> and in one place for quick reference.

Piling up all variable declarations in one place makes for much slower
reference: first, one has has to find the pile, then one has to sift
through the pile.

Local declarations are always instantly visible, making for much quicker
reference. Basically, with local declarations the matter of "reference"
is virtually non-existent: the required knowledge is almost always
naturally and intuitively present within the field of view.

The popular counter-argument stating that "functions have to be small
with small number of variables" is nothing else that a fake advice whose
only purpose is to serve as an emergency brace for the cracking and
crumbling bad practice of piling up variable declarations.

> Then you also know there are no multiple, incompatible
> instances of any local identifier.

This is a classic "solution looking for a problem": a solution for a
problem that does not really exist in the first place.

> 7) I think initialisers are a good idea. Some languages will ensure such
> locals are set to a known starting value, like 0 or 0.0 for static
> types; presumably they think that is a benefit.

It definitely has a benefit: it works as a bootstrapping point for
static/dynamic initialization. I.e. gives you a reliable anchoring
point: something that is guaranteed to be there "even before the
programs starts", without you having to worry about the initialization
and associated ordering issues. No other reason.

And this, of course, does not apply outside of static initialization
contexts. The only thing you achieve by using dummy initializers is
defeat checks performed by static/dynamic analysis tools.

Keith Thompson

unread,
Dec 29, 2021, 5:37:29 PM12/29/21
to
Bart <b...@freeuk.com> writes:
[...]
> 3) Double is perfectly suitable for representing monetary amounts,
> especially in a toy program or for personal use. (I've used it for
> accounts within a small business.)

In a toy program, sure -- and I'd say *only* in a toy program.

Using floating-point for money risks, at best, rare off-by-one errors,
such as a computation yielding a result one cent off from the correct
value. I believe that would be considered unacceptable in any
real-world financial application.

> But what exactly would be a practical alternative here? To import a
> decimal float library, or somehow work with int64 whole cents? (Good
> luck with interest calculations with that.)

If you only need to do calculations that yield whole numbers of cents,
then yes, you can use a wide integer (long long or uint64_t, for
example, or define your own type "in64" if you insist for some reason)
with a scaling factor of 100. If you need to do interest calculations
in the real world (not in a toy program), you need to find out the exact
rules for those calculations and implement them.

[...]

> 5) I can't see an issue in using pow() to do pow(float,int); the
> result will not be exact anyway. What is the alternate, to invent a
> local powint(float,int) routine? Which will probably contain a
> loop. Or change the logic for an accumulative product. pow() is
> simpler!

Integer exponentiation can be done exactly (assuming no overflow).
pow() can suffer floating-point errors. Whether pow() is acceptable
depends on the application and the financial rules governing it.

[...]

Bart

unread,
Dec 29, 2021, 6:09:58 PM12/29/21
to
On 29/12/2021 22:37, Keith Thompson wrote:
> Bart <b...@freeuk.com> writes:
> [...]
>> 3) Double is perfectly suitable for representing monetary amounts,
>> especially in a toy program or for personal use. (I've used it for
>> accounts within a small business.)
>
> In a toy program, sure -- and I'd say *only* in a toy program.
>
> Using floating-point for money risks, at best, rare off-by-one errors,
> such as a computation yielding a result one cent off from the correct
> value. I believe that would be considered unacceptable in any
> real-world financial application.

Real-world doesn't need to mean dealing with trillions of dollars to the
exact cent.

Most businesses work with amounts far smaller. Then I believe the
precision of float64 is enough to emulate the needed rounding methods.

(But I didn't even bother with that. My app anyway had to work with
multliple parallel currencies so if an amount was correct to the nearest
cent on one, it couldn't also be correct in another. But the environment
was also fairly informal.)

>> But what exactly would be a practical alternative here? To import a
>> decimal float library, or somehow work with int64 whole cents? (Good
>> luck with interest calculations with that.)
>
> If you only need to do calculations that yield whole numbers of cents,
> then yes, you can use a wide integer (long long or uint64_t, for
> example, or define your own type "in64" if you insist for some reason)
> with a scaling factor of 100. If you need to do interest calculations
> in the real world (not in a toy program), you need to find out the exact
> rules for those calculations and implement them.

I wouldn't know where to start to apply an interest rate specified to
multiple decimals, to an integer amount, to yield an integer result.

> [...]
>
>> 5) I can't see an issue in using pow() to do pow(float,int); the
>> result will not be exact anyway. What is the alternate, to invent a
>> local powint(float,int) routine? Which will probably contain a
>> loop. Or change the logic for an accumulative product. pow() is
>> simpler!
>
> Integer exponentiation can be done exactly (assuming no overflow).
> pow() can suffer floating-point errors. Whether pow() is acceptable
> depends on the application and the financial rules governing it.

This is integer exponentation of floating point values. The choice is
between repeated multiplications, or calling pow().

The latter is likely done with logs and exp() which can introduce
errors, but if I calculate 3% compound interest monthly over about 80
years, then the results of:

1.03 * 1.03 * ... // 1000 times

and:

pow(1.03, 1000)

differ only in the 16th significant digit.

Scott Lurndal

unread,
Dec 29, 2021, 6:54:21 PM12/29/21
to
Bart <b...@freeuk.com> writes:
>On 29/12/2021 22:37, Keith Thompson wrote:
>> Bart <b...@freeuk.com> writes:
>> [...]
>>> 3) Double is perfectly suitable for representing monetary amounts,
>>> especially in a toy program or for personal use. (I've used it for
>>> accounts within a small business.)
>>
>> In a toy program, sure -- and I'd say *only* in a toy program.
>>
>> Using floating-point for money risks, at best, rare off-by-one errors,
>> such as a computation yielding a result one cent off from the correct
>> value. I believe that would be considered unacceptable in any
>> real-world financial application.
>
>Real-world doesn't need to mean dealing with trillions of dollars to the
>exact cent.

Real-world still uses COBOL for these type of applications.

>
>Most businesses work with amounts far smaller. Then I believe the
>precision of float64 is enough to emulate the needed rounding methods.

Why bother with the error-prone "needed rounding methods". Use uint64_t
and denominate in mills.

If your domain requires larger numbers, widen it to 128 bits.

Keith Thompson

unread,
Dec 29, 2021, 7:10:02 PM12/29/21
to
Bart <b...@freeuk.com> writes:
> On 29/12/2021 22:37, Keith Thompson wrote:
>> Bart <b...@freeuk.com> writes:
>> [...]
>>> 3) Double is perfectly suitable for representing monetary amounts,
>>> especially in a toy program or for personal use. (I've used it for
>>> accounts within a small business.)
>> In a toy program, sure -- and I'd say *only* in a toy program.
>> Using floating-point for money risks, at best, rare off-by-one
>> errors,
>> such as a computation yielding a result one cent off from the correct
>> value. I believe that would be considered unacceptable in any
>> real-world financial application.
>
> Real-world doesn't need to mean dealing with trillions of dollars to
> the exact cent.

But it can mean dealing with thousands of dollars to the exact cent --
and if you get a result of $1234.565001 and round it to $1234.57, when
the rules call for a result of $1234.56, that could be a serious problem.

> Most businesses work with amounts far smaller. Then I believe the
> precision of float64 is enough to emulate the needed rounding methods.

Working with smaller amounts just means that errors like this are rarer,
not that they don't happen.

> (But I didn't even bother with that. My app anyway had to work with
> multliple parallel currencies so if an amount was correct to the
> nearest cent on one, it couldn't also be correct in another. But the
> environment was also fairly informal.)

If "fairly informal" means you don't need results accurate to the cent,
then floating-point might be good enough. But financial institutions
can't afford that.

>>> But what exactly would be a practical alternative here? To import a
>>> decimal float library, or somehow work with int64 whole cents? (Good
>>> luck with interest calculations with that.)
>> If you only need to do calculations that yield whole numbers of
>> cents,
>> then yes, you can use a wide integer (long long or uint64_t, for
>> example, or define your own type "in64" if you insist for some reason)
>> with a scaling factor of 100. If you need to do interest calculations
>> in the real world (not in a toy program), you need to find out the exact
>> rules for those calculations and implement them.
>
> I wouldn't know where to start to apply an interest rate specified to
> multiple decimals, to an integer amount, to yield an integer result.

Neither would I. My understanding is that there are precisely stated
rules that tell you exactly what results a given computation must
produce, for example when computing compound interest. If you're going
to be writing real-world software, where satisfying those rules is a
requirement, you *must* learn those rules first. (I haven't done so and
probably won't.)

>> [...]
>>
>>> 5) I can't see an issue in using pow() to do pow(float,int); the
>>> result will not be exact anyway. What is the alternate, to invent a
>>> local powint(float,int) routine? Which will probably contain a
>>> loop. Or change the logic for an accumulative product. pow() is
>>> simpler!
>> Integer exponentiation can be done exactly (assuming no overflow).
>> pow() can suffer floating-point errors. Whether pow() is acceptable
>> depends on the application and the financial rules governing it.
>
> This is integer exponentation of floating point values. The choice is
> between repeated multiplications, or calling pow().
>
> The latter is likely done with logs and exp() which can introduce
> errors, but if I calculate 3% compound interest monthly over about 80
> years, then the results of:
>
> 1.03 * 1.03 * ... // 1000 times
>
> and:
>
> pow(1.03, 1000)
>
> differ only in the 16th significant digit.

And if that difference in the 16th significant digit happens to
give you a result that's off by one cent after rounding, that's
a problem -- unless your environment is "fairly informal" and you
don't mind sometimes being off by a cent.

Keith Thompson

unread,
Dec 29, 2021, 7:11:57 PM12/29/21
to
Compound interest calculations can give intermediate results that are
not a whole number of cents or even mills, and those results must be
rounded to the nearest cent at some specified point using specified
rules. (And no, I don't know what those rules are.)

Using large integers representing cents or mills is find if all
you're doing is adding and subtracting.

Kaz Kylheku

unread,
Dec 29, 2021, 7:29:44 PM12/29/21
to
On 2021-12-29, Keith Thompson <Keith.S.T...@gmail.com> wrote:
> Bart <b...@freeuk.com> writes:
> [...]
>> 3) Double is perfectly suitable for representing monetary amounts,
>> especially in a toy program or for personal use. (I've used it for
>> accounts within a small business.)
>
> In a toy program, sure -- and I'd say *only* in a toy program.
>
> Using floating-point for money risks, at best, rare off-by-one errors,
> such as a computation yielding a result one cent off from the correct
> value. I believe that would be considered unacceptable in any
> real-world financial application.

The fear is not well-founded.

Let's for a moment set aside astronomical monetary figures, and say
we are just doing accounting for some ordinary business.

Suppose we use the representation that 1.0 == $1.00 USD.

A one-cent error in some ordinary amount like $15,123,13 is gapingly
huge. It's in the 9th digit out of an available 15.

If that were a problem, floating-point would be entirely unsuitable
for engineering and science.

(Side note: consider that people use Microsoft Excel for real financial
work, and go with those numbers.

https://en.wikipedia.org/wiki/Numeric_precision_in_Microsoft_Excel )

The double type can recored 15 decimal digits of precision.
We can store the amount $9,999,999,999,999.99 in a double and
get all of it back. Say, that to be safe against truncation
errors, we scale that back by two orders of magnitude. That
gives us $99,999,999,999.9999 being exactly representable:
99 billion dollars down to a hundredth of a cent. If the
accounting we are doing runs in the cents, we are fine.

What we have to watch out for is the accumulation of errors. For
instance, we cannot just blindly sum an arbitrary long column of
figures, such as the transactions in a long-running account. After every
calculation, we should fastidiously round the result to the nearest
penny. The calculation will already be vastly closer to that
penny, than to any other penny: we are talking about a very tiny
movement.

You can probably get away with letting error accumulate over many
calculations, without any risk that the approximation will jump to the
wrong penny. The safest thing is probably to nip it in the bud:
"snap" the result of every calculation to the "penny grid".

Ideally, every amount recoreded into the double-based ledger
should be that type's closest approximation of the intended
dollars-and-cents amount.

There are some additional issues in that when percentages or other
fractional values are calculated, the accounting system may be governed
by special rules about rounding: when a fractional result lands halfway
between two pennies, which one does it go to?

But this applies regardless of whether we use scaled integers or doubles
for the base amounts, and the requirements could be met with
careful double-based arithmetic.


--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

Kaz Kylheku

unread,
Dec 29, 2021, 7:34:51 PM12/29/21
to
This common intution is incorrect. Financial systems sometimews need to
calculate fractional amounts, like a 15.03% tax or whatever.

And in those situations, you may be required to implement the required
rounding rules: what to do when some fractional result lands halfway
between two pennies: which one does it go to.

You cannot get away from rounding issues just because you used an
integer representation for mills or penies.

Kaz Kylheku

unread,
Dec 29, 2021, 8:00:01 PM12/29/21
to
On 2021-12-30, Keith Thompson <Keith.S.T...@gmail.com> wrote:
> Bart <b...@freeuk.com> writes:
>> On 29/12/2021 22:37, Keith Thompson wrote:
>>> Bart <b...@freeuk.com> writes:
>>> [...]
>>>> 3) Double is perfectly suitable for representing monetary amounts,
>>>> especially in a toy program or for personal use. (I've used it for
>>>> accounts within a small business.)
>>> In a toy program, sure -- and I'd say *only* in a toy program.
>>> Using floating-point for money risks, at best, rare off-by-one
>>> errors,
>>> such as a computation yielding a result one cent off from the correct
>>> value. I believe that would be considered unacceptable in any
>>> real-world financial application.
>>
>> Real-world doesn't need to mean dealing with trillions of dollars to
>> the exact cent.
>
> But it can mean dealing with thousands of dollars to the exact cent --
> and if you get a result of $1234.565001 and round it to $1234.57, when

You cannot get a result of $1234.565... by only additive and subtractive
transactions, such as recording expenses in a ledger whose
denomination is pennies!

(Not unless you neglected to take care of cumulative truncation error
and let that error freely accumulate for a vast number of transactions.
But let's say everything was done right up to the point this $1234.565001
showed up.)

The way you got that half penny in there: 0.56 + 0.005001 is that
you performed a fractional calculation, like calculating a percentage
due to tax or interest or dividend or whatever.

If you calculate a fractional quantity, you must deal with the rounding,
right there and then, based on the applicable requirements.
(Whether or not you're recording monetary amounts using integers!)

For instance, let's suppose that 1234.565001 is the calculation of the
new account balance due to interest earned. The previous balance was
1232.19. Being recorded in the ledger, that fiture is exact. (Or, if we
use doubles, it is a vastly accurate approximation of 1232.19).

We calculated some interest and came up with 1234.565001. But we cannot
record that; the account is to the penny. So what do we do? We apply
some rounding rule, like Banker's Rounding.

If the result were exactly 1234.565, then according ot Banker's
Rounding, we would round down to 1234.56, because 6 is an even digit.
A 5 following an odd digit would go up.

This kind of thing you must handle even if the inputs amounts (other
than the percentage rate) are integers, and so are the outputs.

In the accounting system I wrote for my self-employment activities,
I used integers representing pennies, but fractionals are calculated
with the help of floating-point. I'm not required to deal with precise,d
decimal-based rounding rules, so what I did is this: the fractional
result (measuring pennies) is calculated in double-precision floating
point. Then this cents amount is subject to the C library round()
function to get it to the closest integer (i.e. cent), and then that is
converted to the integer-based monetary type.

I'm confident I could replace that integer-based monetary type with
floating-point and get all the same results (that would be fairly easily
checked, because the system calculates everything from scratch
from the recrded transaction log, every time it is run; my real data
could be converted to a giant regression test case.)

Bart

unread,
Dec 29, 2021, 8:06:35 PM12/29/21
to
On 30/12/2021 00:09, Keith Thompson wrote:
> Bart <b...@freeuk.com> writes:

>> Real-world doesn't need to mean dealing with trillions of dollars to
>> the exact cent.
>
> But it can mean dealing with thousands of dollars to the exact cent --
> and if you get a result of $1234.565001 and round it to $1234.57, when
> the rules call for a result of $1234.56, that could be a serious problem.

With floating point, that result could as easily be just under 1234.565
as just over. If the requirements really are that binary floating point
must have exactly identical results, down to the least significant bit,
even for intermediates so that rounding will always give the same
results too, then that's what you have to do.

But I don't believe the OP's program is in that class. (This is one of a
category of exercises, like hashes, prngs, and reading a line of input
of arbitrary length, where apparently nothing less that a perfect,
industrial-strength professional solution will do.)


>> Most businesses work with amounts far smaller. Then I believe the
>> precision of float64 is enough to emulate the needed rounding methods.
>
> Working with smaller amounts just means that errors like this are rarer,
> not that they don't happen.
>
>> (But I didn't even bother with that. My app anyway had to work with
>> multliple parallel currencies so if an amount was correct to the
>> nearest cent on one, it couldn't also be correct in another. But the
>> environment was also fairly informal.)
>
> If "fairly informal" means you don't need results accurate to the cent,
> then floating-point might be good enough. But financial institutions
> can't afford that.

You can easily get results to the nearest cent. The problem can be when
rounding errors or rounding methods result in discrepancies between
different approaches, or between different programs and languages.

I just don't think this is a programming issue, more one of requirements
and specification.


>> I wouldn't know where to start to apply an interest rate specified to
>> multiple decimals, to an integer amount, to yield an integer result.
>
> Neither would I. My understanding is that there are precisely stated
> rules that tell you exactly what results a given computation must
> produce, for example when computing compound interest. If you're going
> to be writing real-world software, where satisfying those rules is a
> requirement, you *must* learn those rules first. (I haven't done so and
> probably won't.)

Again with real-world. Is this a corner shop or is it Lloyds Bank? Or
someone working out figures for their tax return? Or doing a personal
budget?

(In my country, tax returns only need figures in whole pounds, and don't
need to be that exact. So long as you are not deliberately
misrepresenting by significant amounts, it doesn't matter if they are a
pound either way.)

So, what do you think the OP should do?

Remember this is floating point which is generally not exact: 1.00625
(the factor needed for 7.5% annual interest applied monthly) can't be
represented precisely IEEE754. Using long integers for this isn't a
magic bullet, since you still have to multiply by 1.00625.

As it turns out, I do have a clue how it's done: you multiply by 100625
and divide by 100000.

However, int64 or even int128 don't have enough range to multiply by
100625 N times before dividing by 100000 N times. You have to do the
divide at each step. Which means deciding what to do about the remainder
at each step (especially if negative numbers are involved).

It's back to specifications.

Keith Thompson

unread,
Dec 29, 2021, 8:48:40 PM12/29/21
to
Kaz Kylheku <480-99...@kylheku.com> writes:
> On 2021-12-30, Keith Thompson <Keith.S.T...@gmail.com> wrote:
[...]
>> But it can mean dealing with thousands of dollars to the exact cent --
>> and if you get a result of $1234.565001 and round it to $1234.57, when
>
> You cannot get a result of $1234.565... by only additive and subtractive
> transactions, such as recording expenses in a ledger whose
> denomination is pennies!

I didn't say otherwise.

[...]

James Kuyper

unread,
Dec 29, 2021, 10:45:54 PM12/29/21
to
On 12/29/21 2:33 PM, Andrey Tarasevich wrote:
> On 12/29/2021 9:49 AM, Bart wrote:
...
>> It's not obvious that C's /default/ float type is 'double', nor that
>> 'float' is not the default float type, despite the name!
>
> It doesn't have to be obvious. That's why I state it explicitly.

Stating it explicitly is not as good as providing relevant citations.

That was certainly true in K&R C, where the usual arithmetic conversions
always caused float operands to be converted to double, and the default
argument promotions caused float arguments to be promoted to double.
However, in C90 the usual arithmetic conversions were changed so that
floating-point operands were never converted to a higher precision
unless the other operand had a higher precision.
In C90, none of the standard library functions took a float or long
double argument, but that changed in C99 - all of the math.h functions
now come in three different forms: one suffixed with 'f' that takes
float arguments and/or returns float values, one with no suffix for
double, and on suffixed with 'l' for long double.
C90 also introduced function prototypes. When calling a function with a
prototype declaration in scope (which is now the normal case), the
default argument promotions occur only for the variable parts of
variadic functions. That's the only sense in which "double is the
default" survived past C99.

I'd say that, since C99, it's far from obvious that double is the
default precision, and I'd say that the reason it's not obvious is
because it's no longer true.

Andrey Tarasevich

unread,
Dec 30, 2021, 3:36:11 AM12/30/21
to
On 12/29/2021 7:45 PM, James Kuyper wrote:
>
> I'd say that, since C99, it's far from obvious that double is the
> default precision, and I'd say that the reason it's not obvious is
> because it's no longer true.

A rather obvious fact that an unsuffixed `0.0` is a `double` still
grants a significant degree of defaultness (sic) to `double`.

David Brown

unread,
Dec 30, 2021, 4:19:33 AM12/30/21
to
If you call a function for which there is no prototype (I think it is
insane that non-prototype declarations are still allowed in C, even C18,
but that's a side point) then any arguments of type "float" are promoted
to "double". The same applies to arguments to a function with a
variable number of arguments (in the ellipsis part).

That adds to the "double is default" argument (though I agree with James
that it is much less the default in C99 than it was in C90).



Malcolm McLean

unread,
Dec 30, 2021, 7:48:56 AM12/30/21
to
On Thursday, 30 December 2021 at 01:06:35 UTC, Bart wrote:
>
> (In my country, tax returns only need figures in whole pounds, and don't
> need to be that exact. So long as you are not deliberately
> misrepresenting by significant amounts, it doesn't matter if they are a
> pound either way.)
>
In the UK, the Inland Revenue doesn't care about small amounts of money.

But, if you write a cheque to Inland Revenue, the amount the bank calculates
you have in your account needs to match the amount the program calculates
you have in your account to the exact penny. Not because accountants care
about one penny, but because any discrepancy, however small, could indicate
some more serious mis-accounting.

Bart

unread,
Dec 30, 2021, 7:50:10 AM12/30/21
to
The problem isn't the recommended rounding method, it's repeatability.

With floating point, an intermediate result might end in .4999999993 or
in .5000000007. If the rule is that up to exactly 0.5 rounds down, and
anything beyond rounds up, then it can go either way.

With integer divide, the results should be much more consistent: a
remainder will either be 4999999993 or 5000000007 or whatever, always.

Manfred

unread,
Dec 30, 2021, 12:16:49 PM12/30/21
to
On 12/30/2021 2:06 AM, Bart wrote:
> On 30/12/2021 00:09, Keith Thompson wrote:
>> Bart <b...@freeuk.com> writes:
>
>>> Real-world doesn't need to mean dealing with trillions of dollars to
>>> the exact cent.
>>
>> But it can mean dealing with thousands of dollars to the exact cent --
>> and if you get a result of $1234.565001 and round it to $1234.57, when
>> the rules call for a result of $1234.56, that could be a serious problem.
>
> With floating point, that result could as easily be just under 1234.565
> as just over. If the requirements really are that binary floating point
> must have exactly identical results, down to the least significant bit,
> even for intermediates so that rounding will always give the same
> results too, then that's what you have to do.

Yes, and that's why standard floating point is not suitable for accurate
accounting.

IEEE 754 floating point types are designed for applications where its is
OK to assume that all values are approximated, e.g. measurements of
physical quantities. In this kind of environment the goal is to have
lots of decimal digits and huge scale of representation, some
predictable estimate of the propagation of rounding errors, and
calculation efficiency.
The price of not being able to represent exactly the value 1% (i.e.
0.01) is acceptable, even if this value is so common, as long as the
goals above are achieved.

The financial world has different requirements: they want exact results
whenever possible, and rounding errors, when impossible to avoid, are
thoroughly regulated.
This is because balancing the books down to the exact cent is important
to those guys.
In order to achieve this, they are willing to pay some price in terms of
representable numbers: the whole world uses monetary amounts quantified
down to the cent (2 decimal digits). In a similar fashion, conversion
ratios like currency exchange rates are conventionally expressed in a
fixed amount of significant digits (I think it's 6 or 7 digits), and the
same is likely to hold for interest rates as well. There's some upper
limit to the largest monetary amount to handle (e.g. the global wealth),
and so on.
Finally, calculations need not be super fast: a monetary transaction
involves much less computation than, say, modeling a magnetic field.

All in all, it's no surprise that the financial world uses different
data types than floating point, like e.g. the Decimal type.

Kaz Kylheku

unread,
Dec 30, 2021, 3:48:08 PM12/30/21
to
On 2021-12-30, Keith Thompson <Keith.S.T...@gmail.com> wrote:
> Kaz Kylheku <480-99...@kylheku.com> writes:
>> On 2021-12-30, Keith Thompson <Keith.S.T...@gmail.com> wrote:
> [...]
>>> But it can mean dealing with thousands of dollars to the exact cent --
>>> and if you get a result of $1234.565001 and round it to $1234.57, when
>>
>> You cannot get a result of $1234.565... by only additive and subtractive
>> transactions, such as recording expenses in a ledger whose
>> denomination is pennies!
>
> I didn't say otherwise.

No, you didn't; but so then that doesn't really speak to the issue of
should the base amounts be integers or floating-point.

An integer-based system can have fractional values come up and
has to deal with them.

Kaz Kylheku

unread,
Dec 30, 2021, 4:04:19 PM12/30/21
to
This is fairly straightforward, actually. Suppose we are workign with
dollars and cents, and have a $0.0049999993 or $0.005000000007 result.

We do not just look at the 4 or 5 digit we look at (at least) one more
digit, and round that off first:

$0.0049... -> $0.0050 -> $0.005

$0.0050... -> $0.0050 -> $0.005

Now that we have our mill (tenth of a cent) denomination rounded off
using conventional arithmetic rounding, we then look at the tenth of a
cent digit and round to the cent.

Here we can use a specific rounding rule like Banker's for instance, if
we are so inclined:

$0.005 -> $0.00 // digit before 5 is 0, which is even, so down

This could be done with character processing: format the floating point
number to a certain number of fixed digits after the decimal point, just
letting sprintf (or whatever function) doing its built-in rounding.
Then, using the resulting decimal, implement the exact pencil-and-paper
rounding method.

Or it can all be done with just floating-point calculations.

The key point is that both of the original numbers $0.0049999993 and
$0.005000000007 are understood to be floating-point approximations
representing precisely half a cent, and with a little care we can get
the rounding to work accordingly: we can get that half a cent value
first, and then implement the required rounding to the cent.

Bart

unread,
Dec 30, 2021, 9:13:36 PM12/30/21
to
I think I thought of some similar idea at one time, but I never tested
it for real, and now I'm not so sure.

For example, if instead of 4999993 and 5000007, you have 4499993 and 4500007

Now it's not so clear whether that first digit ends up as 4 or 5.

Bart

unread,
Dec 30, 2021, 9:20:19 PM12/30/21
to
Well, I have my own unlimited precision decimal floating point library.
But I would still think it a vast overkill for the mortgage calculation
exercise of the OP's.

It also doesn't magically solve all issues. You'd have problems with
exactly representing 1/3 of 1% for example, or 1/3 of £1 (approx 33.33p
or exactly 6/8d in old money), or doing any similar kind of division.

Manfred

unread,
Dec 31, 2021, 12:25:19 PM12/31/21
to
It's not really a matter of unlimited precision, see my remark above
about the amount of significant digits.

>
> It also doesn't magically solve all issues. You'd have problems with
> exactly representing 1/3 of 1% for example, or 1/3 of £1 (approx 33.33p
> or exactly 6/8d in old money), or doing any similar kind of division.

No finite arithmetic can solve these issues - whatever the base, you
will always have some divisor that yields an unrepresentable result.

But, as I said, the financial world has addressed this by limiting the
number of significant digits - decimal digits - in conversion factors. I
had this information, for international currency exchange rates, from
someone who used to make up his living writing financial software, and I
wouldn't be surprised at all if the same applied to other factors as
well, like interest rates.

I can understand that the difference between an interest rate of 1.03%
and 1.030000001% is negligible (the business manager may be perfectly
happy with a discrete choice between say 1.03% and 1.03001%), but
inexact books balancing is not, even (maybe especially) if it's about a
cent for a business worth billions.

James Kuyper

unread,
Dec 31, 2021, 6:14:02 PM12/31/21
to
On 12/31/2021 3:20 AM, Bart wrote:
...
> It also doesn't magically solve all issues. You'd have problems with
> exactly representing 1/3 of 1% for example, or 1/3 of £1 (approx 33.33p
> or exactly 6/8d in old money), or doing any similar kind of division.

The issue is not about accurately representing the results of all
possible divisions. It's about getting the legally mandated results.
Whenever money is involved, there's people trying to use every possible
trick to get more of it than they're actually entitled to. As a result,
regulatory agencies have gotten heavily involved in such issues,
specifying precisely how financial calculations should be performed.
I've heard from second-hand sources what kinds of requirements those
agencies have imposed, but I have not been able to locate any first-hand
sources. It hardly matters, because the detailed requirements vary from
one country to another, and even between different industries. However,
they all are based upon the same idea:

Every quantity you need to calculate must, in effect, be stored as an
integer with a scaling factor that is an integer power of 10, usually
negative. All calculations are performed as if using ordinary integer
arithmetic, with the appropriate scaling factor for the result. I say
"in effect", because on some platforms you can achieve the same result
by relying upon hardware support for decimal floating point.

The point is, if you don't use either scaled integers or decimal
floating point or something similar, you won't always round the results
of your calculation the same way as is required by the relevant
regulations. Even if it only happens rarely and only involves a tiny
amount of money, if the regulators find out they will come after you
because the fact that you calculated it incorrectly is taken as evidence
hinting that you were at least sloppy with your calculations, and at
worst, may have been trying to do something fraudulent.

Bart

unread,
Dec 31, 2021, 6:59:28 PM12/31/21
to
Then the regulators should themselves provide a library that performs
arithmetic or other calculations according to their standards.

That's if someone is working on software where this is an actual necessity.

I'm just fed up with people always bringing this up on forums when some
quantity might involve money. Those advanced requirements are heavily
dependent on application areas, and little to do with basic programming,
especially when a language doesn't provide the special types (scaled
integers etc) that might be needed.


james...@alumni.caltech.edu

unread,
Dec 31, 2021, 7:21:04 PM12/31/21
to
On Friday, December 31, 2021 at 6:59:28 PM UTC-5, Bart wrote:
> On 31/12/2021 23:13, James Kuyper wrote:
...
> > The point is, if you don't use either scaled integers or decimal
> > floating point or something similar, you won't always round the results
> > of your calculation the same way as is required by the relevant
> > regulations. Even if it only happens rarely and only involves a tiny
> > amount of money, if the regulators find out they will come after you
> > because the fact that you calculated it incorrectly is taken as evidence
> > hinting that you were at least sloppy with your calculations, and at
> > worst, may have been trying to do something fraudulent.
> Then the regulators should themselves provide a library that performs
> arithmetic or other calculations according to their standards.

That's not their responsibility. And there's a large body of existing
software that does exactly what's needed. It's mostly specialized accounting
software. If you're using commercial accounting software, it probably
conforms to the relevant requirements.

> That's if someone is working on software where this is an actual necessity.

Correct. Different financial calculations are subject to different regulations,
and in many cases the relevant regulations are lenient enough to allow
people to ignore these issues. However, that's generally not the case with
mortgages.

> I'm just fed up with people always bringing this up on forums when some
> quantity might involve money. Those advanced requirements are heavily
> dependent on application areas, and little to do with basic programming,
> especially when a language doesn't provide the special types (scaled
> integers etc) that might be needed.

If you feel that way, then you should be pleased to know that _decimal32,
_decimal64, and _decimal128 are being considered for inclusion in the next
version of the C standard. There were added precisely because of these
issues.

Mortgages are heavily regulated (at least in the US, and I presume also in
most first-world countries where mortgages are even permitted). Any
calculations like the ones the OP was performing should be disclaimed as
only estimates, unless they handle the rounding of the results in the manner
required by law.

Keith Thompson

unread,
Dec 31, 2021, 7:35:16 PM12/31/21
to
Are you sure they don't? I'm not.

And if they don't -- is the ISO C committee obligated to provide a
conforming C implementation?

It's not my field and I don't know the details. Unlike you, I admit that.

> That's if someone is working on software where this is an actual necessity.
>
> I'm just fed up with people always bringing this up on forums when
> some quantity might involve money. Those advanced requirements are
> heavily dependent on application areas, and little to do with basic
> programming, especially when a language doesn't provide the special
> types (scaled integers etc) that might be needed.

As several of us have acknowledged, using floating-point for money is
probably fine for toy or introductory programs. It's not ok for any
application that interacts with financial regulations. And I'm fed up
with you whining when anyone points that out.

Bart

unread,
Dec 31, 2021, 7:54:01 PM12/31/21
to
On 01/01/2022 00:35, Keith Thompson wrote:
> Bart <b...@freeuk.com> writes:
>> On 31/12/2021 23:13, James Kuyper wrote:

>> Then the regulators should themselves provide a library that performs
>> arithmetic or other calculations according to their standards.
>
> Are you sure they don't? I'm not.

If they do then you use that and be done with it. But it's little to
with learning to program.


> And if they don't -- is the ISO C committee obligated to provide a
> conforming C implementation?
>
> It's not my field and I don't know the details. Unlike you, I admit that.
>
>> That's if someone is working on software where this is an actual necessity.
>>
>> I'm just fed up with people always bringing this up on forums when
>> some quantity might involve money. Those advanced requirements are
>> heavily dependent on application areas, and little to do with basic
>> programming, especially when a language doesn't provide the special
>> types (scaled integers etc) that might be needed.
>
> As several of us have acknowledged, using floating-point for money is
> probably fine for toy or introductory programs. It's not ok for any
> application that interacts with financial regulations. And I'm fed up
> with you whining when anyone points that out.

I'm sure that many other industries have their own standards and their
own requirements when it comes to numbers representing values relevant
to their fields.

But no, it's always the ones representing money!

As a matter of interest (no pun!), if you were writing some program in
C, to do say stock reports on sales in your shop (so it's not exactly a
toy, and you're not a beginner), what type would you use for those
monetary amounts?

Say the info is input from CSV file for example.


Keith Thompson

unread,
Dec 31, 2021, 11:22:50 PM12/31/21
to
That seems to be what comes up most often here.

Somebody asks about what's probably a student-level program that deals
with money. Somebody else mentions that there are rules in place that
have to be followed if you're dealing with money in a professional
context. I think that's a good thing to be aware of even if you don't
have to deal with all the issues directly.

It's an example, one among many, of an area where the inexactness of
floating-point calculations can cause problems -- and that's something
that most programmers need to be aware of.

And then you tell us you're "fed up" with it being mentioned. Too bad.

> As a matter of interest (no pun!), if you were writing some program in
> C, to do say stock reports on sales in your shop (so it's not exactly
> a toy, and you're not a beginner), what type would you use for those
> monetary amounts?
>
> Say the info is input from CSV file for example.

I don't know. If all I have to worry about is adding and subtracting
quantities that are always whole numbers of cents, I'd probably use a
sufficiently wide integer type with a scale factor of 100. If I had to
deal with computations that can yield fractional cents, I'd find out
what the detailed requirements are, and ask what libraries are available
to help implement those requirements.

Malcolm McLean

unread,
Jan 1, 2022, 1:04:14 AM1/1/22
to
Why not use a double, and represent the amount in cents? That gives you
53 bits. However if Biden's stimulus package goes through and the USA
experiences hyperinflation, then the program will scale to sausages
costing quadrillions of dollars - people won't worry about small errors
in the cents under such circumstances. And if you need to represent a
fractional number of cents, you can still do that, admittedly with some
issues, but without knowing the exact rules and circumstances under
which fractions of cents can be created, it's impossible to get away from
those issues.

Bart

unread,
Jan 1, 2022, 11:53:13 AM1/1/22
to
No, I'm fed up with it being a deal-breaker. OK, here it was one point
many, but still, AT specifically said:

'Stop using "regular" floating point types for representing monetary
quantities. They are not good for that purpose.'

I probably do most such calculations on my Casio; that doesn't have a
special type either.

>> As a matter of interest (no pun!), if you were writing some program in
>> C, to do say stock reports on sales in your shop (so it's not exactly
>> a toy, and you're not a beginner), what type would you use for those
>> monetary amounts?
>>
>> Say the info is input from CSV file for example.
>
> I don't know. If all I have to worry about is adding and subtracting
> quantities that are always whole numbers of cents, I'd probably use a
> sufficiently wide integer type with a scale factor of 100. If I had to
> deal with computations that can yield fractional cents, I'd find out
> what the detailed requirements are, and ask what libraries are available
> to help implement those requirements.

It's for your personal use to help in managing your shop. No officialdom
is involved.

Yes, you'd probably use doubles representing dollar amounts (not cents),
display results to 2 decimals, and perhaps round intermediate results to
two decimals.


Bart

unread,
Jan 1, 2022, 1:19:14 PM1/1/22
to
On 29/12/2021 19:33, Andrey Tarasevich wrote:
> On 12/29/2021 9:49 AM, Bart wrote:

>> 2) Nothing wrong with using 'float' when it's appropropriate. Many
>> libraries eg. OpenGL make extensive use of float.
>
> That's just emphasizes my the point. The main purpose of "small" types,
> like `float`, `short`, char as plain integer, bit-fields etc. is to save
> memory in quantitatively massive data structures.
>
> That's exactly what OpenGL is intended to work with: massive amounts of
> geometric data. That's why it opts for `float` Everything else is just a
> consequence of that.

It has to give acceptable results. Presumably float's accuracy is
sufficient for that. Using less memory, and being somewhat faster, are
bonuses.

>> and in one place for quick reference.
>
> Piling up all variable declarations in one place makes for much slower
> reference: first, one has has to find the pile, then one has to sift
> through the pile.
>
> Local declarations are always instantly visible,

That's the problem! If trying to appreciate the algorithm, the types and
attributes of the variables are secondary.

Also, the type is only applied to the first occurence. You see
'interest' being used on a subsequent; what type is it? You glance at
the top of the function - it's not there! You have to scan backwards
from where you are until you encounter a matching declaration.

But then, if 'interest' /was/ declared at the top, now you are wasting
type doing that scan.

(This is a simple program. Have a look at something like the amalgmated
source file of sqlite3.c, where they really take your style on board.

You also have to contend with names with appear identical but have
subtle differences, or the same name declared more than once in the same
function. If you miss the first declaration on that backwards scan, you
may end up at the wrong one.

Look at the two declarations of pKeyInfo in this extract:

if( pPart || pOrderBy ){
int nPart = (pPart ? pPart->nExpr : 0);
int addrGoto = 0;
int addrJump = 0;
int nPeer = (pOrderBy ? pOrderBy->nExpr : 0);

if( pPart ){
int regNewPart = reg + pMWin->nBufferCol;
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart,
pMWin->regPart,nPart);
sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
VdbeCoverageEqNe(v);
windowAggFinal(pParse, pMWin, 1);
if( pOrderBy ){
addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
}
}

if( pOrderBy ){
int regNewPeer = reg + pMWin->nBufferCol + nPart;
int regPeer = pMWin->regPart + nPart;

if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
if( pMWin->eType==TK_RANGE ){
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse,
pOrderBy, 0, 0);
addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer,
nPeer);
sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
VdbeCoverage(v);
}else{
addrJump = 0;
}
windowAggFinal(pParse, pMWin, pMWin->eStart==TK_CURRENT);
if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
}

Note that I've helped you by isolating the lines with both declarations,
but some functions are considerably longer, such as one of 750 lines
with 3 versions of "pPk".

Yet another delight is a 7000-line function, which uses names such as
"pcx" and "pCx" (with different types), or "pVtab" (of type sqlite_vtab)
and "pVTab" (of type VTABLE!).

All the 300+ locals in this function are listed below. Not all identical
names have the same type (eg. 'len' exists as 'long' and also 'int').

If all were listed at the top, there would necessarily be fewer as you
couldn't have duplicates (some will be labels), but if sorted like this
must surely be easier to find than scanning 7000 lines of dense code.


----------------------------------------------

(Sorry the types are not in proper C syntax. Names with void types are
labels.)

abort_due_to_error void
abort_due_to_interrupt void
aEQb [6]const uchar
affinity schar
aFlag [2]const ushort
aGTb [6]const uchar
alreadyExists int
aLTb [6]const uchar
aMem ref struct sqlite3_value
and_logic [9]const uchar
aOffset ref uint
aOp ref struct VdbeOp
apArg ref ref struct sqlite3_value
apArg ref ref struct sqlite3_value
aPermute ref int
aRes [3]int
arithmetic_result_is_null void
aRoot ref int
aZero [16]uchar
azType [4]const ref const schar
bIntint schar
bRev int
c int
c int
check_for_interrupt void
cnt int
cnt int
compare_op void
Compiling sqlite3.c to sqlite3.exe
db ref struct sqlite3
desiredAutoCommit int
encoding uchar
eNew int
eOld int
eqOnly int
exists int
file_format int
flags ushort
flags1 ushort
flags3 ushort
fp_math void
i int
i int
i int
i int
i int
i int
i int
i int
i int
i int
iA llong
iA llong
iAddr uint
iB llong
iB llong
iCompare int
iCookie int
iDb int
iDb int
iDb int
iDb int
idx int
ii int
ii int
iKey llong
iKey ullong
iMeta int
iMeta int
iMoved int
initData struct (ref struct sqlite3 db,ref ref
schar pzErrMsg,int iDb,int rc,uint mInitFlags)
iPrior uint
iQuery int
iRollback int
iSavepoint int
iSet int
isLegacy int
isNotInt int
isSchemaChange int
isTransaction int
isWriteLock uchar
j int
jump_to_p2 void
jump_to_p2_and_check_for_interrupt void
len int
len uint
n int
n int
n int
n int
n int
n uint
nArg int
nArg int
nByte int
nByte llong
nByte llong
nChange int
nData ullong
nEntry llong
nErr int
newMax uint
next_tail void
nField int
nField int
nField int
nHdr int
nKeyCol int
nMem int
nName int
no_mem void
nProgressLimit uint
nRoot int
nullFlag ushort
nVarint int
nVmStep uint
nZero llong
oc int
offset64 ullong
On line: 140
op uchar
op_column_corrupt void
op_column_out void
op_column_read_header void
open_cursor_set_hints void
opflags int
or_logic [9]const uchar
origFlags ushort
p ref struct Vdbe
p1 int
p1 int
p1 int
p1 int
p2 int
p2 int
p2 int
p2 int
pArgc ref struct sqlite3_value
pBt ref struct Btree
pBt ref struct Btree
pBt ref struct Btree
pBt ref struct Btree
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pC ref struct VdbeCursor
pCaller ref struct VdbeOp
pcDest int
pColl ref struct CollSeq
pCrsr ref struct BtCursor
pCrsr ref struct BtCursor
pCrsr ref struct BtCursor
pCrsr ref struct BtCursor
pCrsr ref struct BtCursor
pCrsr ref struct BtCursor
pCrsr ref struct BtCursor
pCrsr ref struct BtCursor
pCtx ref struct sqlite3_context
pCtx ref struct sqlite3_context
pCtx ref struct sqlite3_context
pCtx ref struct sqlite3_context
pCur ref struct VdbeCursor
pCur ref struct VdbeCursor
pCur ref struct VdbeCursor
pCur ref struct VdbeCursor
pCur ref struct VdbeCursor
pcx int
pCx ref struct VdbeCursor
pCx ref struct VdbeCursor
pCx ref struct VdbeCursor
pCx ref struct VdbeCursor
pData ref struct sqlite3_value
pData0 ref struct sqlite3_value
pDb ref struct Db
pDb ref struct Db
pDb ref struct Db
pDest ref struct sqlite3_value
pDest ref struct sqlite3_value
pEnd ref struct sqlite3_value
pFrame ref struct VdbeFrame
pFrame ref struct VdbeFrame
pFrame ref struct VdbeFrame
pFrame ref struct VdbeFrame
pFrame ref struct VdbeFrame
pFree ref struct UnpackedRecord
pgno int
pgno int
pIdxKey ref struct UnpackedRecord
pIn ref struct sqlite3_value
pIn1 ref struct sqlite3_value
pIn2 ref struct sqlite3_value
pIn3 ref struct sqlite3_value
pKey ref struct sqlite3_value
pKeyInfo ref struct KeyInfo
pKeyInfo ref struct KeyInfo
pKeyInfo ref struct KeyInfo
pLast ref struct sqlite3_value
pMem ref struct sqlite3_value
pMem ref struct sqlite3_value
pMem ref struct sqlite3_value
pMem ref struct sqlite3_value
pMem ref struct sqlite3_value
pMem ref struct sqlite3_value
pModule ref struct sqlite3_module
pModule ref struct sqlite3_module
pModule ref struct sqlite3_module
pModule ref struct sqlite3_module
pModule ref struct sqlite3_module
pModule ref struct sqlite3_module
pName ref struct sqlite3_value
pnErr ref struct sqlite3_value
pNew ref struct Savepoint
pOp ref struct VdbeOp
pOrig ref struct VdbeCursor
pOut ref struct sqlite3_value
pPager ref struct Pager
pProgram ref struct SubProgram
pQuery ref struct sqlite3_value
pRec ref struct sqlite3_value
pReg ref struct sqlite3_value
pRt ref struct sqlite3_value
pSavepoint ref struct Savepoint
pTab ref struct Table
pTab ref struct Table
pTabCur ref struct VdbeCursor
pTmp ref struct Savepoint
pVar ref struct sqlite3_value
pVCur ref struct sqlite3_vtab_cursor
pVCur ref struct sqlite3_vtab_cursor
pVtab ref struct sqlite3_vtab
pVtab ref struct sqlite3_vtab
pVtab ref struct sqlite3_vtab
pVtab ref struct sqlite3_vtab
pVtab ref struct sqlite3_vtab
pVtab ref struct sqlite3_vtab
pVtab ref struct sqlite3_vtab
pVTab ref struct VTable
pX ref struct Btree
pX ref struct sqlite3_value
r struct (ref struct KeyInfo pKeyInfo,ref
struct sqlite3_value aMem,ushort nField,schar default_rc,uchar
errCode,schar r1,schar r2,uchar eqSeen)
r struct (ref struct KeyInfo pKeyInfo,ref
struct sqlite3_value aMem,ushort nField,schar default_rc,uchar
errCode,schar r1,schar r2,uchar eqSeen)
r struct (ref struct KeyInfo pKeyInfo,ref
struct sqlite3_value aMem,ushort nField,schar default_rc,uchar
errCode,schar r1,schar r2,uchar eqSeen)
r struct (ref struct KeyInfo pKeyInfo,ref
struct sqlite3_value aMem,ushort nField,schar default_rc,uchar
errCode,schar r1,schar r2,uchar eqSeen)
rA double
rB double
rc int
res int
res int
res int
res int
res int
res int
res int
res int
res int
res int
res int
res int
res int
res2 int
resetSchemaOnFault uchar
rowid llong
rowid llong
sContext struct (ref struct sqlite3_value pOut,ref
struct FuncDef pFunc,ref struct sqlite3_value pMem,ref struct Vdbe
pVdbe,int iOp,int isError,uchar skipFlag,uchar argc,[1]ref struct
sqlite3_value argv)
seek_not_found void
seekResult int
serial_type uint
sMem struct (union MemValue u,ushort
flags,uchar enc,uchar eSubtype,int n,ref schar z,ref schar zMalloc,int
szMalloc,uint uTemp,ref struct sqlite3 db,ref proc(ref void)void xDel)
sMem struct (union MemValue u,ushort
flags,uchar enc,uchar eSubtype,int n,ref schar z,ref schar zMalloc,int
szMalloc,uint uTemp,ref struct sqlite3 db,ref proc(ref void)void xDel)
sz llong
t ref void
t uint
takeJump int
too_big void
type1 ushort
type2 ushort
uA ullong
v llong
v llong
v1 int
v2 int
val llong
vdbe_return void
vfsFlags const int
vtabOnConflict uchar
wrFlag int
x llong
x ref proc(ref void,ref const schar)void
x struct (ref const void pKey,llong
nKey,ref const void pData,ref struct sqlite3_value aMem,ushort nMem,int
nData,int nZero)
x struct (ref const void pKey,llong
nKey,ref const void pData,ref struct sqlite3_value aMem,ushort nMem,int
nData,int nZero)
z ref const schar
z ref schar
z ref schar
z ref schar
zAffinity ref const schar
zAffinity ref schar
zData ref const uchar
zDb ref const schar
zDb ref const schar
zEndHdr ref const uchar
zFilename ref const schar
zHdr ref const uchar
zMaster ref const schar
zName ref schar
zNewRecord ref uchar
zSql ref schar
zTab ref const schar
zTrace ref schar

Bart

unread,
Jan 1, 2022, 2:34:10 PM1/1/22
to
On 01/01/2022 18:19, Bart wrote:
> On 29/12/2021 19:33, Andrey Tarasevich wrote:

>> Local declarations are always instantly visible,

[Summary of local variables from a giant sqlite3.c function]

> nByte                          int
> nByte                          llong
> nByte                          llong

Don't get caught out!

> nField                         int
> nField                         int
> nField                         int

> p1                             int
> p1                             int
> p1                             int
> p1                             int

> pCrsr                          ref struct BtCursor
> pCrsr                          ref struct BtCursor
> pCrsr                          ref struct BtCursor
> pCrsr                          ref struct BtCursor
> pCrsr                          ref struct BtCursor
> pCrsr                          ref struct BtCursor
> pCrsr                          ref struct BtCursor
> pCrsr                          ref struct BtCursor

> pFrame                         ref struct VdbeFrame
> pFrame                         ref struct VdbeFrame
> pFrame                         ref struct VdbeFrame
> pFrame                         ref struct VdbeFrame
> pFrame                         ref struct VdbeFrame

> res                            int
> res                            int
> res                            int
> res                            int
> res                            int
> res                            int
> res                            int
> res                            int
> res                            int
> res                            int
> res                            int
> res                            int
> res                            int
> res2                           int

The above sets of identical names highlight other problems with local
declarations. Take the 24 versions of 'pC', with struct VdbeFrame*;
suppose the type needed to change; now you have to change 24 versions of it.

But, you won't know there are 24 versions; you will need to hunt them
down. Maybe some of the 24 are unrelated in use, and need to stay the
same type. But the function has mixed them all up in the same way that
all instances of a literal '1200' can be mixed up in a program.


> x                              llong
> x                              ref proc(ref void,ref const schar)void
> x                              struct (ref const void pKey,llong

I haven't analysed how these names relate to each within the block
structure. But suppose the x's are in nested blocks, and one declaration
is missed out. That one may be shadowing an outer 'x' - of the wrong
type. With luck, its used will cause a type error, but if not...


> nKey,ref const void pData,ref struct sqlite3_value aMem,ushort nMem,int
> nData,int nZero)
> x                              struct (ref const void pKey,llong
> nKey,ref const void pData,ref struct sqlite3_value aMem,ushort nMem,int
> nData,int nZero)

> z                              ref const schar
> z                              ref schar
> z                              ref schar
> z                              ref schar

... or it ends up with a version that has a missing 'const'. Even when
they're all the same type, you could end writing or reading into the
wrong variable.

Keith Thompson

unread,
Jan 1, 2022, 5:59:43 PM1/1/22
to
Malcolm McLean <malcolm.ar...@gmail.com> writes:
> On Saturday, 1 January 2022 at 04:22:50 UTC, Keith Thompson wrote:
[...]
>> I don't know. If all I have to worry about is adding and subtracting
>> quantities that are always whole numbers of cents, I'd probably use a
>> sufficiently wide integer type with a scale factor of 100. If I had to
>> deal with computations that can yield fractional cents, I'd find out
>> what the detailed requirements are, and ask what libraries are available
>> to help implement those requirements.
>>
> Why not use a double, and represent the amount in cents? That gives you
> 53 bits.

Why? long long gives you 63 bits. When floating-point gives you inexact
results, it does so silently. What advantage does double give you over
long long?

[politics deleted]
> And if you need to represent a
> fractional number of cents, you can still do that, admittedly with some
> issues, but without knowing the exact rules and circumstances under
> which fractions of cents can be created, it's impossible to get away from
> those issues.

Which is why you need to follow the relevant rules, which means you need
to find out what they are before you start writing code.

Malcolm McLean

unread,
Jan 2, 2022, 8:22:18 PM1/2/22
to
On Saturday, 1 January 2022 at 22:59:43 UTC, Keith Thompson wrote:
> Malcolm McLean <malcolm.ar...@gmail.com> writes:
> > On Saturday, 1 January 2022 at 04:22:50 UTC, Keith Thompson wrote:
> [...]
> >> I don't know. If all I have to worry about is adding and subtracting
> >> quantities that are always whole numbers of cents, I'd probably use a
> >> sufficiently wide integer type with a scale factor of 100. If I had to
> >> deal with computations that can yield fractional cents, I'd find out
> >> what the detailed requirements are, and ask what libraries are available
> >> to help implement those requirements.
> >>
> > Why not use a double, and represent the amount in cents? That gives you
> > 53 bits.
> Why? long long gives you 63 bits. When floating-point gives you inexact
> results, it does so silently. What advantage does double give you over
> long long?
>
> [politics deleted]
>
You've deleted the reason. Which is inherently political.
Some crazy politican decides to do what is called "monetary financing",
basically printing money rather than collecting taxes. Result, runaway
inflation, and a hamburger costs 4 quadrillion dollars. 63 bits can
no longer cope (for a decent-sized burger joint). Floating point however
degrades gracefully until you hit figures so high that even hyperinflation
is unlikely to reach them.

Keith Thompson

unread,
Jan 2, 2022, 11:49:56 PM1/2/22
to
If you're operating under regulations that say that "degrading
gracefully" is acceptable, you can use floating-point.

I won't discuss politics here.

Mateusz Viste

unread,
Jan 3, 2022, 3:22:46 AM1/3/22
to
2022-01-02 at 17:22 -0800, Malcolm McLean wrote:
> Result, runaway inflation, and a hamburger costs 4 quadrillion
> dollars. 63 bits can no longer cope (for a decent-sized burger
> joint). Floating point however degrades gracefully until you hit
> figures so high that even hyperinflation is unlikely to reach them.

The scenario that you describe is nothing that we haven't seen already.
The solution is to reevaluate the currency. We (Poles) did it in 1995
with a ratio of 1000:1, ie. 1000 old złotys (1000 PLZ) became one new
złoty (1 PLN). The French did something similar in 1960, where 100 old
francs became 1 new franc.

Shortly said, this is not a technical problem, and even if it was, then
it is not up to the programmer to decide if degrading currency is
acceptable or not, national banks issue strict regulations about how
things should be done in such occasion. In other words, bringing the
subject up in a technical group does not make any sense.

Mateusz

Malcolm McLean

unread,
Jan 3, 2022, 6:09:44 AM1/3/22
to
When hamburgers cost 4 quadrillion dollars, it's likely that everyone has more
to worry about than financial software rounding errors of a few cents.
However at least the system still works after a fashion. Systems using fixed
point will fall over, compounding the social chaos.

Malcolm McLean

unread,
Jan 3, 2022, 6:30:26 AM1/3/22
to
It happened it Weimar Germany. 4 trillion marks to the dollar. But that was
before computerisation. We haven't had hyperinflation in an advanced major
economy since, but there's a real risk of it now with current policies being
pursued in the United States.
The authorities might issue strict instructions on what is to be done as
fixed point system fail to cope and the electronic accounting and transaction
clearing system starts to fall over. But it will make Y2K look like a trivial problem.

Much better to have a system that works after a fashion without needing to
be re-programmed.

Scott Lurndal

unread,
Jan 3, 2022, 11:22:46 AM1/3/22
to
Malcolm McLean <malcolm.ar...@gmail.com> writes:

>When hamburgers cost 4 quadrillion dollars,

Leave out of this group the absurdist political rhetoric, please.

David Brown

unread,
Jan 3, 2022, 11:22:49 AM1/3/22
to
On 03/01/2022 12:30, Malcolm McLean wrote:
> On Monday, 3 January 2022 at 08:22:46 UTC, Mateusz Viste wrote:
>> 2022-01-02 at 17:22 -0800, Malcolm McLean wrote:
>>> Result, runaway inflation, and a hamburger costs 4 quadrillion
>>> dollars. 63 bits can no longer cope (for a decent-sized burger
>>> joint). Floating point however degrades gracefully until you hit
>>> figures so high that even hyperinflation is unlikely to reach them.
>> The scenario that you describe is nothing that we haven't seen already.
>> The solution is to reevaluate the currency. We (Poles) did it in 1995
>> with a ratio of 1000:1, ie. 1000 old złotys (1000 PLZ) became one new
>> złoty (1 PLN). The French did something similar in 1960, where 100 old
>> francs became 1 new franc.
>>
>> Shortly said, this is not a technical problem, and even if it was, then
>> it is not up to the programmer to decide if degrading currency is
>> acceptable or not, national banks issue strict regulations about how
>> things should be done in such occasion. In other words, bringing the
>> subject up in a technical group does not make any sense.
>>
> It happened it Weimar Germany. 4 trillion marks to the dollar. But that was
> before computerisation. We haven't had hyperinflation in an advanced major

It is one thing to bring up real-world historical examples of when large
numbers were needed to hold currency values - it shows that small ranged
types may not be sufficient (although I hope most people here are
sufficiently well-educated to know that hyperinflation has occurred).

But please drop the politics. If you want to discuss fiscal policy in
some country, I am sure there are newsgroups suited to that. But unless
some politician is trying to influence the C programming language, it is
off-topic here.

Malcolm McLean

unread,
Jan 3, 2022, 11:48:30 AM1/3/22
to
The point is that hyperinfaltion is not just some remote theoretical possibility.
It could easily happen, given current policy. So programmers need to be
aware of this possibility and be prepared for it.
I'm not saying it will definitely happen, and I'm not saying that there are no
arguments for destroying the currency and resetting. That would be moving
from programming to politics.
What I am saying is that progammer need to be prepared for what the politicans
may decide.

Mateusz Viste

unread,
Jan 3, 2022, 12:42:40 PM1/3/22
to
2022-01-03 at 08:48 -0800, Malcolm McLean wrote:
> The point is that hyperinfaltion is not just some remote theoretical
> possibility. It could easily happen, given current policy. So
> programmers need to be aware of this possibility and be prepared for
> it.

I believe you are very much mistaken. Programmers need not to worry
about life, they only need to fullfill project requirements. THESE may
or may not take into account real-life scenarios like the one you keep
mentioning. Drafting project requirements is not a subject that matches
this group's activity.

Mateusz

Bart

unread,
Jan 3, 2022, 12:59:19 PM1/3/22
to
Unless it involves local accounting practices in rounding monetary amounts!

Meanwhile, float and double can represent monetary units up to about
1e38 or 1e308, but not to the nearest dollar or even cent when the sums
get big enough.

Scott Lurndal

unread,
Jan 3, 2022, 1:13:14 PM1/3/22
to
Malcolm McLean <malcolm.ar...@gmail.com> writes:
>On Monday, 3 January 2022 at 16:22:49 UTC, David Brown wrote:
>ted to know that hyperinflation has occurred).
>>
>> But please drop the politics. If you want to discuss fiscal policy in
>> some country, I am sure there are newsgroups suited to that

>The point is that hyperinfaltion is not just

No, the point is that this is not the proper venue for your political/economic
views. Please stop.

Malcolm McLean

unread,
Jan 3, 2022, 2:09:44 PM1/3/22
to
You can take the view that you are just a code monkey. So if the instructions
say store the date as two digits, you store the date as two digits, and let other
people worry what happens when the date rolls round to year 2000.

But many programmers don't work in that type of environment.

Mateusz Viste

unread,
Jan 3, 2022, 2:32:12 PM1/3/22
to
2022-01-03 at 11:09 -0800, Malcolm McLean wrote:
> But many programmers don't work in that type of environment.

You mean these programmers that code up a financial application that
looses dollars here and there if the sums' too big, instead of
failing with a clear error message? Yes, I know the type. I try to
avoid having them in my teams.

Mateusz

Malcolm McLean

unread,
Jan 3, 2022, 2:58:57 PM1/3/22
to
I don't code programs that deal with amounts of money.

But I'd be extremely concerned about this. Basically you're taking the system
down if inflation creates a numeric overflow. That might be OK in some
situations, but for others, it could be a disaster. You buy a burger, and suddenly
all the tills go down because the day's takings have exceeded 63 bits. Not
a good look.

Kenny McCormack

unread,
Jan 3, 2022, 3:05:14 PM1/3/22
to
In article <P0HAJ.143568$7D4.1...@fx37.iad>,
Why does it hurt you so much to see Malcolm's posts here?
Why is it so important - why is your little psyche so damaged - that you
feel the need to comment?

The motto of Usenet is: If you don't like something, just ignore it.
Pretend it didn't happen. It's the only way to keep your sanity.

P.S. And, you're about to do a "I know what I am, but what are you?"
routine at me, can it. This is a legitimate inquiry. I really want to
know how you got to be so damaged.

--
The motto of the GOP "base": You can't *be* a billionaire, but at least you
can vote like one.

Keith Thompson

unread,
Jan 3, 2022, 5:42:32 PM1/3/22
to
Malcolm McLean <malcolm.ar...@gmail.com> writes:
> On Monday, 3 January 2022 at 04:49:56 UTC, Keith Thompson wrote:
[...]
>> I won't discuss politics here.
>>
> When hamburgers [more politics]

Malcolm, knock it off.

Malcolm McLean

unread,
Jan 3, 2022, 5:52:47 PM1/3/22
to
On Monday, 3 January 2022 at 22:42:32 UTC, Keith Thompson wrote:
> Malcolm McLean <malcolm.ar...@gmail.com> writes:
> > On Monday, 3 January 2022 at 04:49:56 UTC, Keith Thompson wrote:
> [...]
> >> I won't discuss politics here.
> >>
> > When hamburgers [more politics]
>
> Malcolm, knock it off.
>
Don't you see that that if you are discussing the representation of monetary
values, there are a few obvious things to consider? Whether they are real or
discrete is one. Another is range. What's the maximum value that we might
need to represent?

Kenny McCormack

unread,
Jan 3, 2022, 5:54:28 PM1/3/22
to
In article <8735m41...@nosuchdomain.example.com>,
Keith Thompson <Keith.S.T...@gmail.com> wrote:
>Malcolm McLean <malcolm.ar...@gmail.com> writes:
>> On Monday, 3 January 2022 at 04:49:56 UTC, Keith Thompson wrote:
>[...]
>>> I won't discuss politics here.
>>>
>> When hamburgers [more politics]
>
>Malcolm, knock it off.

Keith...

Why so butt-hurt?

--
The randomly chosen signature file that would have appeared here is more than 4
lines long. As such, it violates one or more Usenet RFCs. In order to remain
in compliance with said RFCs, the actual sig can be found at the following URL:
http://user.xmission.com/~gazelle/Sigs/GodDelusion

Kenny McCormack

unread,
Jan 3, 2022, 5:58:43 PM1/3/22
to
In article <b2918a53-acd6-4c46...@googlegroups.com>,
Malcolm, ...

You probably would have been fine - and the Not-Cs would have just ignored
you - if you just hadn't used the B-word (*) in your first post in the
sub-thread. Unfortunately, this is an error from which you cannot recover.

(*) Biden

--
12% of Americans think that Joan of Arc was Noah's wife.

David Brown

unread,
Jan 3, 2022, 6:18:18 PM1/3/22
to
You can discuss that without the politics. It is enough to note that
some countries have suffered hyperinflation so severe that 64-bit
integers would not be sufficient for currency, and leave it there. Let
those that are interested in the history (and that includes me) look it
up or discuss it elsewhere. Incorrect historical claims and wild
political conspiracy theories are of no use or interest here. Is it so
difficult for you to appreciate the difference?


Keith Thompson

unread,
Jan 3, 2022, 6:24:46 PM1/3/22
to
Malcolm McLean <malcolm.ar...@gmail.com> writes:
> On Monday, 3 January 2022 at 22:42:32 UTC, Keith Thompson wrote:
>> Malcolm McLean <malcolm.ar...@gmail.com> writes:
>> > On Monday, 3 January 2022 at 04:49:56 UTC, Keith Thompson wrote:
>> [...]
>> >> I won't discuss politics here.
>> >>
>> > When hamburgers [more politics]
>>
>> Malcolm, knock it off.
>>
> Don't you see that that if you are discussing the representation of monetary
> values, there are a few obvious things to consider? Whether they are real or
> discrete is one. Another is range. What's the maximum value that we might
> need to represent?

Of course I see that. And if you want to talk about how hyperinflation
and/or extremely large quantities might affect financial calculations.
If you assume an upper bound on a quantity, it's best to make that
assumption explicit.

It's your assertions that specific currently proposed policies will lead
to hyperinflation that are off-topic and annoying.

Malcolm McLean

unread,
Jan 3, 2022, 6:43:46 PM1/3/22
to
On Monday, 3 January 2022 at 23:24:46 UTC, Keith Thompson wrote:
> Malcolm McLean <malcolm.ar...@gmail.com> writes:
> > On Monday, 3 January 2022 at 22:42:32 UTC, Keith Thompson wrote:
> >> Malcolm McLean <malcolm.ar...@gmail.com> writes:
> >> > On Monday, 3 January 2022 at 04:49:56 UTC, Keith Thompson wrote:
> >> [...]
> >> >> I won't discuss politics here.
> >> >>
> >> > When hamburgers [more politics]
> >>
> >> Malcolm, knock it off.
> >>
> > Don't you see that that if you are discussing the representation of monetary
> > values, there are a few obvious things to consider? Whether they are real or
> > discrete is one. Another is range. What's the maximum value that we might
> > need to represent?
> Of course I see that. And if you want to talk about how hyperinflation
> and/or extremely large quantities might affect financial calculations.
> If you assume an upper bound on a quantity, it's best to make that
> assumption explicit.
>
Maybe not. If we assume that a hamburger costs about a dollar in today's money,
in Weimar Germany the mark got up to 4 trillion to the dollar before being
abandoned. So clearly we need to plan for a burger costing at least 4 trillion
dollars. But how high could it go? Very hard to answer. But a double will handle
values of up to 10^308. It's hard to see even hyperinflation getting that high
before the monetary system totally collapses. So we're probably safe as far
as range is concerned with a double.
A double also has 53 bits of precision. that allows values of up to about 10^16
to be represented exactly. That's 10^14 is we store values in cents. So is that
enough? That's a much harder call. If you are dealing with hamburger joints,
then you can have quite a bit of inflation before you fail to be able to represent
as many hamburgers as you could reasonably sell. Then you start dropping
cents. But by the time you start dropping cents, the cent will be a notional unit
of currency. All the coins will have been melted down for metal. So is 53 bits
enough? I don't write programs that deal withh amounts of money, so I don't
really know.
However if I had to choose a C type, I'd probably go for integer representation
of amounts in cents, using a double.
>
> It's your assertions that specific currently proposed policies will lead
> to hyperinflation that are off-topic and annoying.
>
As far as C programmers are concerned, the question is, is hyperinflation
something that happened in Weimar Germany back in the olden days,
and could never happen in the modern US. Or is it a real and present
danger. Is this something we should be thinking about?

If you ask some qualified people, I think you'll find that they are extremely
worried.

Öö Tiib

unread,
Jan 4, 2022, 12:19:22 AM1/4/22
to
In every successful project where I've participated several things about its
data and functionality were illogical. Properties of records were misplaced,
not belonging to that record, having inadequate value ranges, duplicated
but not synchronized in other records, some crucial information was missing
and had to be figured by strange heuristics and so on. Modules did things
that these were not supposed to do or left things not done these were
supposed to do. Often such issues were known and cursed at but left
there and sometimes even some new such weirdness were added.

It is not because engineers participating deserved to be called code monkeys.
Infallible people are available nowhere. So perfect is worst enemy of good.
Who tries to make perfect things with usual fallible people right away
gets nothing done. Meanwhile software that is made good enough for
subset of its purposes will be used and improved over time. So it brings
profit right now.

One day part of that profit can be invested back to inevitable huge work
of replacing the Euros in it to German Goldens or whatever and it will be
done. Who can predict if there will be need for it and when and what will
it cost? It will be stuck in inability to proceed:
<https://en.wikipedia.org/wiki/Analysis_paralysis> No profit.

Mateusz Viste

unread,
Jan 4, 2022, 3:23:15 AM1/4/22
to
2022-01-03 at 15:43 -0800, Malcolm McLean wrote:
> will have been melted down for metal. So is 53 bits enough? I don't
> write programs that deal withh amounts of money, so I don't really
> know. However if I had to choose a C type, I'd probably go for
> integer representation of amounts in cents, using a double.

A double is nice to represent not-100%-precise fractional units,
it really does not have its place in any kind of financial
program, esp. if the goal is storing cents anyway.

A probably sane approach could be not to store the numbers at
all, but rely entirely on the database engine for handling and storing
them, while the C program would only process strings. The rationale
here would be that if the database cannot store the numbers properly,
then you are screwed anyway. Also, you could upgrade/fix the database
in the future without having to touch the program.
PostgreSQL has a nice "numeric" type that could be well suited for the
job at hand.

Alternatively, use uint64_t or _uint128_t if you really have to crunch
the numbers yourself. These types are proper integers that have well
defined boundaries and are easy to manipulate.

In any case, introducing doubles in such system will lead to entirely
new classes of problems, since even doing seemingly trivial math with
doubles can easily become a man-hour consuming trap.

> As far as C programmers are concerned, the question is, is
> hyperinflation something that happened in Weimar Germany back in the
> olden days, and could never happen in the modern US.

Again, not something that "C programmers" should be concerned with.
Unless you mean some kind of indie programmer that is at the same time
a financial expert, businessman and salesman, ie. a one-man-do-it-all
startup guy. But this guy won't have time for drafting apocalyptic
scenarios in his program, he will have to deliver something basic yet
efficient and appealing and sell it quickly if he wants to survive. Any
other scenario will include specifications written by someone that
knows what he's doing, ie. a domain expert.

Mateusz

David Brown

unread,
Jan 4, 2022, 3:32:11 AM1/4/22
to
If you are writing a simple example or test program, use whatever type
suits. If you are writing a single-purpose or single-user program, use
whatever type suits. These would be integer types if you want to store
integer cents (or pennies, or whatever), or doubles if you want to make
it easy to do interest rate calculations. This will be fine to tell you
the values involved - and the real legal tracking of money will be done
by banks and accountants using software that follows the required rules.

If you are writing a serious program, you find the rules for the
jurisdictions that are relevant. You don't make guesses based on vague
memories of school-time history classes or think "cents is good enough".
In some places, the rules involved are much more demanding - I know
that for some currencies, you need to track six digits after the decimal
point.

>>
>> It's your assertions that specific currently proposed policies will lead
>> to hyperinflation that are off-topic and annoying.
>>
> As far as C programmers are concerned, the question is, is hyperinflation
> something that happened in Weimar Germany back in the olden days,
> and could never happen in the modern US. Or is it a real and present
> danger. Is this something we should be thinking about?
>

No, it is not something you need to think about as a C programmer. You
write the code you need - and for an example program like the OP's, it
doesn't need any connection to reality. On the other hand, if you want
to write serious financial software, you follow the rules required - you
don't invent them.

> If you ask some qualified people, I think you'll find that they are extremely
> worried.
>

Could modern economies spiral into hyperinflation? Of course it is
possible. We've seen it before, worse and more recently than the Weimar
Republic. If you want to learn about and discuss this, find an
appropriate place to do so rather than mixing limited historical points
with paranoid conspiracy theories in a programming group. Or perhaps
find a qualified person and ask them, to put your mind at ease. Just be
sure to look for that qualified person in an appropriate place - not
comp.lang.c, and not some Facebook post by your Auntie's neighbour's
friend whose dad is a janitor in a bank so he should know.


Malcolm McLean

unread,
Jan 4, 2022, 4:31:05 AM1/4/22
to
On Tuesday, 4 January 2022 at 08:32:11 UTC, David Brown wrote:
> On 04/01/2022 00:43, Malcolm McLean wrote:
>
> > As far as C programmers are concerned, the question is, is hyperinflation
> > something that happened in Weimar Germany back in the olden days,
> > and could never happen in the modern US. Or is it a real and present
> > danger. Is this something we should be thinking about?
> >
> No, it is not something you need to think about as a C programmer. You
> write the code you need - and for an example program like the OP's, it
> doesn't need any connection to reality. On the other hand, if you want
> to write serious financial software, you follow the rules required - you
> don't invent them.
>
So you are also thinking like a code monkey. A code monkey is not a "bad
programmer". It's a model of development in which the programmer is as
far as possible de-skilled. So if the instructions say "store the date as
two digits", he is meant to store the date as two digits. It's not his job to
raise the question of what happens when we roll over to year 2000 with
his superiors. He's just the coder.
> > If you ask some qualified people, I think you'll find that they are extremely
> > worried.
> >
> Could modern economies spiral into hyperinflation? Of course it is
> possible. We've seen it before, worse and more recently than the Weimar
> Republic. If you want to learn about and discuss this, find an
> appropriate place to do so rather than mixing limited historical points
> with paranoid conspiracy theories in a programming group. Or perhaps
> find a qualified person and ask them, to put your mind at ease. Just be
> sure to look for that qualified person in an appropriate place - not
> comp.lang.c, and not some Facebook post by your Auntie's neighbour's
> friend whose dad is a janitor in a bank so he should know.
>
A conspiracy theory alleges that there is some secret plot to, in this
case, hyperinflate the currency. You don't have to believe that. Policy
that is a matter of public record is enough. We've seen hyperinflation in
places like Zimbabwe and Venuzuela, but not in an advanced, major
Western economy since Weimar. The partial exception is Israel in the
1980s, but it didn't have a hard currency at the time in the 1980s
(I believe). However that is going too far off topic.

Malcolm McLean

unread,
Jan 4, 2022, 7:27:24 AM1/4/22
to
The poor man. Imagine mention of your name causing such angst.

Kenny McCormack

unread,
Jan 4, 2022, 8:39:38 AM1/4/22
to
In article <9f5ceaf1-e9b6-4c0c...@googlegroups.com>,
Malcolm McLean <malcolm.ar...@gmail.com> wrote:
...
>The poor man. Imagine mention of his name causing such angst.

You're talking about poor Keith, right?

Yes, Keith has his, shall we say, issues...

--

Prayer has no place in the public schools, just like facts
have no place in organized religion.
-- Superintendent Chalmers

David Brown

unread,
Jan 4, 2022, 8:55:29 AM1/4/22
to
On 04/01/2022 10:30, Malcolm McLean wrote:

There is a wise saying - when you are in a hole, stop digging. You are
giving bad advice, justifying it with outlandish and unrealistic
political gibberish and your own misunderstandings of what people do in
the world of software development.

If you were employed as a programmer and asked to develop software that
complied with the fiscal laws of a country, but decided to implement it
in a different way because of your fairyland political ideas, you'd be
out of a job.

If that's the way /you/ want to live, that's up to you. But please stop
suggesting such attitudes to others that might be new to the programming
world and misunderstand your postings as good advice.

> However that is going too far off topic.
>

So let that be an end of this thread, now that you have pulled it so far
away from anything related to C, to the OP's questions, or to any
on-topic diversions that came out of the thread.

Malcolm McLean

unread,
Jan 4, 2022, 9:21:15 AM1/4/22
to
On Tuesday, 4 January 2022 at 13:55:29 UTC, David Brown wrote:
> On 04/01/2022 10:30, Malcolm McLean wrote:
>
> There is a wise saying - when you are in a hole, stop digging. You are
> giving bad advice, justifying it with outlandish and unrealistic
> political gibberish and your own misunderstandings of what people do in
> the world of software development.
>
> If you were employed as a programmer and asked to develop software that
> complied with the fiscal laws of a country, but decided to implement it
> in a different way because of your fairyland political ideas, you'd be
> out of a job.
>
The code monkey asks what the instructions are, and follows them. That's
because he's a low level person with limited responsibilities.
The software engineer looks at the instructions, and asks if they will
fulfil the requirement of the project, or if there is some problem with them.
In this case, whether the representation he has been asked to use for
monetary values will cope with hyperinflation.
What action he then takes depends on the status of the instructions, who
issued them, if there is a standard procedure for raising defect reports,
and, crucially, whether he believes that the scenario in which the instructions
will fail is a likely one and of importance to the project, or very far-fetched.

Engineering requires intelligence.
>
> If that's the way /you/ want to live, that's up to you. But please stop
> suggesting such attitudes to others that might be new to the programming
> world and misunderstand your postings as good advice.
>
Some people are happy with your model of development. It does have something
to be said for it. But it's not a professional attitude.
>
> > However that is going too far off topic.
> >
> So let that be an end of this thread, now that you have pulled it so far
> away from anything related to C, to the OP's questions, or to any
> on-topic diversions that came out of the thread.
>
I've kept very closely related to C at all times. Most useful programs deal with
things in the outside world. So if you are writing a C program which isn't
something that is purely computational, you need to discuss that thing.
In this case we are discussing money and how to represent it.

Keith leapt on the "B word" for his own reasons.

Kenny McCormack

unread,
Jan 4, 2022, 10:03:55 AM1/4/22
to
In article <2a287c23-ad75-45e8...@googlegroups.com>,
Malcolm McLean <malcolm.ar...@gmail.com> wrote:
...
>A conspiracy theory alleges that there is some secret plot to, in this
>case, hyperinflate the currency. You don't have to believe that. Policy
>that is a matter of public record is enough. We've seen hyperinflation in
>places like Zimbabwe and Venuzuela, but not in an advanced, major Western
>economy since Weimar. The partial exception is Israel in the 1980s, but
>it didn't have a hard currency at the time in the 1980s (I believe).
>However that is going too far off topic.

In the 21st century, the term "conspiracy theory" has lost all meaning -
not that it really had any in the first place.

In the 21st century, it just means "Any political idea that I don't like".

That is the sense in which the other idiot (the one to whom you were
responding) meant it.

--
This is the GOP's problem. When you're at the beginning of the year
and you've got nine Democrats running for the nomination, maybe one or
two of them are Dennis Kucinich. When you have nine Republicans, seven
or eight of them are Michelle Bachmann.

David Brown

unread,
Jan 4, 2022, 10:50:01 AM1/4/22
to
On 04/01/2022 15:21, Malcolm McLean wrote:

<snip>

You've moved beyond off-topic posts into bad excuses and poorly-veiled
insults. Please leave it. If you feel there is something worth
discussing, my email address is valid.

Kenny McCormack

unread,
Jan 4, 2022, 10:57:11 AM1/4/22
to
In article <sr1qat$60c$1...@dont-email.me>,
Why you so butt-hurt by this?

--
The randomly chosen signature file that would have appeared here is more than 4
lines long. As such, it violates one or more Usenet RFCs. In order to remain
in compliance with said RFCs, the actual sig can be found at the following URL:
http://user.xmission.com/~gazelle/Sigs/Mandela

Guillaume

unread,
Jan 4, 2022, 12:18:36 PM1/4/22
to
Le 04/01/2022 à 09:23, Mateusz Viste a écrit :
> 2022-01-03 at 15:43 -0800, Malcolm McLean wrote:
>> will have been melted down for metal. So is 53 bits enough? I don't
>> write programs that deal withh amounts of money, so I don't really
>> know. However if I had to choose a C type, I'd probably go for
>> integer representation of amounts in cents, using a double.
>
> A double is nice to represent not-100%-precise fractional units,
> it really does not have its place in any kind of financial
> program, esp. if the goal is storing cents anyway.

Absolutely. Using FP for financial calculations is for people who don't
know arithmetics. Maybe teaching some math (again) to CS students would
help, here? :)

At the very least, if you absolutely want to use FP for this, at least
use decimal FP. Although it can still have precision issues due to its
floating point nature, at least rounding will not yield gross errors.

And if you're OK with using integers, but are afraid fixed-size ones (at
least 64 bits) could be an unacceptable limit down the road, just use
arbitrary precision arithmetic. And you don't need to hand-implement
that, unless it's your thing. GNU GMP is great.

One of the many problems with FP can be easily shown. Apart from the
usual unability to represent some numbers exactly, they also have an
inherent problem when, for instance, adding very small numbers to very
large ones.

Of course if all you do is writing "toy programs", use whatever. But
somehow, if this is a student work, I would have a problem with calling
student exercises "toy programs". That would certainly give them the
wrong idea about how to do things properly. If they once used FP for
financial operations, even on a small exercise, how can you know they
won't do the same later on at work when having to write code dealing
with financial calculations?

Malcolm McLean

unread,
Jan 4, 2022, 1:35:36 PM1/4/22
to
The central difficulty is that people have no problem with the idea
that an integer type should hold an amount in cents. But for some reason
they think that a floating point type must hold an amount in dollars.
Probably because an amount of 110 cents is always written as 1.10
dollars for the end-user.

Once you make that leap, you'll see that you can use floating point types to
represent integers.
It is loading more messages.
0 new messages