2

I have to calculate the values of a hot plate and have it accurate only to the first decimal place. I am stumped on trying to figure out how to check all the array values if they changed. I found out that 724 runs made no change after that to the 4th decimal (how many were being printed).

Is there a way to compare doubles variables only up to the n-th decimal place?

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

const int ARRAY_SIZE = 20;
const int NEIGHBORS = 4;

void initialize(double hot_plate[][ARRAY_SIZE]);
bool writeFile(const double HOT_PLATE[][ARRAY_SIZE],
               const string FILE_NAME);

double sum_cell(const double HOT_PLATE[][ARRAY_SIZE],
                const int CELL_X, const int CELL_Y);

int main()
{
    double hot_plate[ARRAY_SIZE][ARRAY_SIZE];

    initialize(hot_plate);

    string file_name = "hot_plate.csv";

    //accuracy up to 4 decmials
    int runs = 724;
    while ( runs > 0)
    {
        for (int i = 0; i < ARRAY_SIZE; i++)
        {
            for (int j = 0; j < ARRAY_SIZE; j++)
            {
                if (i > 0 && i < ARRAY_SIZE - 1 && j > 0 && j < ARRAY_SIZE - 1)
                {
                    hot_plate[i][j] = sum_cell(hot_plate, j, i);
                }
            }
        }
        runs--;
    }

    if (writeFile(hot_plate, file_name))
    {
        cout << "File wrote correctly\n";
    }
    else
    {
        cout << "The file did not write!\n";
    }

    //system("pause");

    return 0;
}

////////////////////////////////////////////////////////////////////////////////
//////////////////////////// Completed Code ////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

double sum_cell(const double HOT_PLATE[][ARRAY_SIZE],
                const int CELL_X, const int CELL_Y)
{
    /* This code should never go out of bounds as it's in an if statement
       if (i > 0 && i < ARRAY_SIZE - 1 && j > 0 && j < ARRAY_SIZE - 1)
    */
    double cell_num = HOT_PLATE[CELL_X - 1][CELL_Y]; // Top
    cell_num += HOT_PLATE[CELL_X][CELL_Y - 1]; // Left
    cell_num += HOT_PLATE[CELL_X][CELL_Y + 1]; // Right
    cell_num += HOT_PLATE[CELL_X + 1][CELL_Y]; // Bottom

    cell_num /= NEIGHBORS;

    return cell_num;
}

// setup the Array so all values are defined when starting
void initialize(double hot_plate[][ARRAY_SIZE])
{
    for (int i = 0; i < ARRAY_SIZE; i++)
    {
        for (int j = 0; j < ARRAY_SIZE; j++)
        {
            if (i == 0 || i == ARRAY_SIZE - 1)
            {
                if (j == 0 || j == ARRAY_SIZE - 1)
                {
                    hot_plate[i][j] = 0.0;
                }
                else
                {
                    hot_plate[i][j] = 100.0;
                }
            }
            else
            {
                hot_plate[i][j] = 0.0;
            }
        }
    }
}

// Write the data to the CSV file
bool writeFile(const double HOT_PLATE[][ARRAY_SIZE],
               const string FILE_NAME)
{
   // open the file
   ofstream fout(FILE_NAME);
   if (fout.fail())
      return false;

   for (int i = 0; i < ARRAY_SIZE; i++)
   {
       for (int j = 0; j < ARRAY_SIZE; j++)
       {
           fout << HOT_PLATE[i][j];
           if ( j < ARRAY_SIZE - 1)
           {
               fout << ", ";
           }
           else if (i != ARRAY_SIZE - 1)
           {
               fout << endl;
           }
       }
   }

   // close the input stream from the file.
   fout.close();
   return true;
}
krizzo
  • 1,823
  • 5
  • 30
  • 52
  • So where are you actually comparing doubles in that code? – K-ballo Sep 20 '11 at 16:19
  • I'm not comparing them yet. That's what I was trying to figure out. given_cell=10.210 after the math it could be 10.356, 10.209 or 10.092. This is to find the steady-state temperature distribution on the hot_plate. I only need accuracy up to the 10ths place but didn't know how to compare doubles like that. – krizzo Sep 20 '11 at 16:30

5 Answers5

6

Is there a way to compare doubles variables only up to the n-th decimal place?

Yes there is, check whether the absolute value of the difference between them is less than 10^-n.

K-ballo
  • 80,396
  • 20
  • 159
  • 169
1

Check comparing floating point numbers and this post on deniweb.

Kashyap
  • 15,354
  • 13
  • 64
  • 103
1

With this function

double getUpToDecPlace (double value, int decPlace)
{
    int dec = 1;
    for (int i = 0; i < decPlace; i++)
    {
        dec *= 10;
    }
    return floor(value*dec + 0.5)/dec;
}

which would return 12.35 for getUpToDecPlace(12.345678, 2), you can compare doubles up to an arbitrary decimal place:

double var1 = 12.345678;
double var2 = 12.351234;
bool comp1 = (getUpToDecPlace(var1, 2) == getUpToDecPlace(var2, 2)); // true
bool comp2 = (getUpToDecPlace(var1, 3) == getUpToDecPlace(var2, 3)); // false
Desmond Hume
  • 8,037
  • 14
  • 65
  • 112
1

There are so many problems here.

  1. You are updating the hot_plate array in-place. So some of the values you use from the 'previous generation' have already been updated in the current generation! You have to compute each generation in a separate array, and then copy it back to the 'master' hot_plate array.

  2. If you want the final result accurate in the first decimal place, it's not enough to continue until the values don't change by more than 0.1. For instance, some values might change by more than 0.05 for ten more generations, which would amount to a change of more than 0.5. In fact, this is a very tricky issue: it requires a global analysis of how the initial conditions evolve over time.

  3. Are you sure you have sum_cell right? The temperature of hot_plate[i][j] at the next generation should surely depend on the current value of hot_plate[i][j], and not just on its neighbours?

Also, this looks a bit silly:

for (int i = 0; i < ARRAY_SIZE; i++)
{
    for (int j = 0; j < ARRAY_SIZE; j++)
    {
        if (i > 0 && i < ARRAY_SIZE - 1 && j > 0 && j < ARRAY_SIZE - 1)

I suggest the equivalent formulation:

for (int i = 1; i < ARRAY_SIZE - 1; i++)
{
    for (int j = 1; j < ARRAY_SIZE - 1; j++)

As for testing equality to the nth decimal place, other posters have covered that.

TonyK
  • 16,761
  • 4
  • 37
  • 72
  • I was wondering if I should keep a duplicate of the array instead of just running though it, I'll fix that and see if it changes the numbers. The simple requirement was to take the sum average of all neighbors which sum_cell does. Coding style requires the brackets, I don't personally like them and that could be cut down as you said. Thanks for pointers. – krizzo Sep 20 '11 at 16:51
  • @LF4: I'm not talking about the brackets! Look again. – TonyK Sep 20 '11 at 16:54
  • Oh haha yeah my bad, now I see what changed. That would be a better solution. Thanks for pointing that out. – krizzo Sep 21 '11 at 13:50
0

Store the measured FP value as a scaled integer:

ix = int(fp * 10000)

You can then do direct comparisons with the required precision.

sizzzzlerz
  • 4,277
  • 3
  • 27
  • 35
  • This is wrong. You want 123.4567 and 123.456699 to be considered equal to 4 decimal places, but they would fail your test. – TonyK Sep 20 '11 at 16:52
  • Add 0.00005 before taking the int. The technique is still valid. – sizzzzlerz Sep 20 '11 at 17:09
  • Exactly what are you suggesting? `int ((fp+0.00005)*10000)`? But this just shifts the problem: now 123.45675 and 123.4567499 will be considered different. The technique is useless. – TonyK Sep 20 '11 at 17:28