Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compare String representations of doubles if it ends in .00

I am currently writing test cases for a ContentProvider in my application (which is a checkbook recording app) and I am having a test fail when it is comparing double values. The best way to illustrate this is with code. I have this function that returns ContentValues for an account object to be inserted into the database:

private ContentValues getAccountContentValues(){
   String testName = "Capital One";
   double testStartingBalance = 3000.00;

   ContentValues accountValues = new ContentValues();
   accountValues.put(AccountEntry.COLUMN_NAME, testName);
   accountValues.put(AccountEntry.COLUMN_BALANCE, testStartingBalance);

   return accountValues;
}

Inside a function testInsertReadProvider() I have the following code, to insert that account:

// Get Account values and write them
ContentValues accountValues = getAccountContentValues();
Uri accountInsertUri = 
        mContext.getContentResolver().insert(AccountEntry.CONTENT_URI, accountValues);
long accountRowId = ContentUris.parseId(accountInsertUri);

// Verify we got a row back
assertTrue(accountRowId > 0);

// A cursor is the primary interface to the query results
Cursor cursor = mContext.getContentResolver().query(
        AccountEntry.CONTENT_URI, // Table name
        null, // columns to be returned. Null returns all columns
        null, // Columns for where clause
        null, // Values for where clause
        null // Sort order
);

// Validate the information read from the database.
validateCursor(cursor, accountValues);
cursor.close();

The validate cursor function takes a cursor and a ContentValues and loops through them using a map to compare each value. This is a strategy I learned from following a Udacity tutorial on creating Android applications, so maybe there is a better way to compare them instead of as strings, but it looks like this:

void validateCursor(Cursor valueCursor, ContentValues expectedValues){
   assertTrue(valueCursor.moveToFirst());

   Set<Map.Entry<String, Object>> valueSet = expectedValues.valueSet();

   for(Map.Entry<String, Object> entry : valueSet){
       String columnName = entry.getKey();
       int idx = valueCursor.getColumnIndex(columnName);
       assertFalse(idx == -1);
       String expectedValue = entry.getValue().toString();
       assertEquals(expectedValue, valueCursor.getString(idx));
   }
   valueCursor.close();
}

The test is failing when the assertEquals() line is called, and I get the following error message:

junit.framework.ComparisonFailure: expected:<3000[.0]> but was:<3000[]>

It looks like the cursor.getString() method is truncating the decimal places if they are equal to 0. If I try this test using a value of 3000.01 it works fine. Is SQLite responsible for dropping the unnecessary zeroes? Can I change the assertEquals() in some way so that those two values are treated the same?

like image 999
AdamMc331 Avatar asked Apr 16 '15 16:04

AdamMc331


People also ask

Can you use == to compare doubles?

Using the == Operator As a result, we can't have an exact representation of most double values in our computers. They must be rounded to be saved. In that case, comparing both values with the == operator would produce a wrong result. For this reason, we must use a more complex comparison algorithm.

Can you compare strings with == C?

You can't compare strings in C with ==, because the C compiler does not really have a clue about strings beyond a string-literal.


2 Answers

You should transfer them to doubles using

double d1 = Double.parseDouble(text) double d2 = Double.parseDouble(text2)

Do the same for second string and compare results. You can also use abs to be sure there is no difference because of representation:

assertTrue(Math.abs(d1 -d2) < 0.00001);

EDIT:

I would go for something like this:

try
{
   double d1 = Double.parseDouble(text);
   double d2 = Double.parseDouble(text2);
   assertTrue(Math.abs(d1-d2) < 0.00001);

}catch(NumberFormatException e)
{
    assertEquals(text, text2);
}
like image 117
Kuba Avatar answered Sep 29 '22 12:09

Kuba


If you really feel like comparing strings, than you will have to hack it a little bit, but there is an alternative below.

First, here's the TL;DR; code:

    for(Map.Entry<String, Object> entry : valueSet){
        String columnName = entry.getKey();
        int idx = valueCursor.getColumnIndex(columnName);
        assertFalse(idx == -1);
        String expectedValue = entry.getValue().toString();
        if((Cursor.FIELD_TYPE_FLOAT == valueCursor.getType(idx))
                && valueCursor.getDouble(idx) % 1 == 0){
            assertEquals(expectedValue, valueCursor.getString(idx) + ".0");
        } else {
            assertEquals(expectedValue, valueCursor.getString(idx));
        }
    }

What is probably happening is that each instance of your double is at the end being projected to String by a different toString() method. That's what causing the irregularities.

You can either hack it as I outlined above or do the comparison in a switch statement specifically designed for each type (IMHO a better solution).

Here is the Switch version:

for(Map.Entry<String, Object> entry : valueSet){
    String columnName = entry.getKey();
    int idx = valueCursor.getColumnIndex(columnName);
    assertFalse(idx == -1);
    switch(valueCursor.getType(idx)) {
        case Cursor.FIELD_TYPE_FLOAT:
            assertEquals(entry.getValue(), valueCursor.getDouble(idx));
            break;
        case Cursor.FIELD_TYPE_INTEGER:
            //assertEquals(entry.getValue(), valueCursor.getInt(idx)); // didn't work
            //assertTrue((new Integer((int)entry.getValue())).equals(valueCursor.getInt(idx)));
            assertEquals(Integer.parseInt(entry.getValue().toString()), valueCursor.getInt(idx));
        case Cursor.FIELD_TYPE_STRING:
            assertEquals(entry.getValue(), valueCursor.getString(idx));
            break;
        default:
            assertEquals(entry.getValue().toString(), valueCursor.getString(idx));
    }
}
like image 37
nana Avatar answered Sep 29 '22 10:09

nana