Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VBA: Testing for perfect cubes

I'm trying to write a simple function in VBA that will test a real value and output a string result if it's a perfect cube. Here's my code:

Function PerfectCubeTest(x as Double)

    If (x) ^ (1 / 3) = Int(x) Then
        PerfectCubeTest = "Perfect"
    Else
        PerfectCubeTest = "Flawed"
    End If

End Function

As you can see, I'm using a simple if statement to test if the cube root of a value is equal to its integer portion (i.e. no remainder). I tried testing the function with some perfect cubes (1, 8, 27, 64, 125), but it only works for the number 1. Any other value spits out the "Flawed" case. Any idea what's wrong here?

like image 514
heygivethatback Avatar asked Mar 08 '17 18:03

heygivethatback


2 Answers

You are testing whether the cube is equal to the double supplied.

So for 8 you would be testing whether 2 = 8.

EDIT: Also found a floating point issue. To resolve we will round the decimals a little to try and overcome the issue.

Change to the following:

Function PerfectCubeTest(x As Double)

    If Round((x) ^ (1 / 3), 10) = Round((x) ^ (1 / 3), 0) Then
        PerfectCubeTest = "Perfect"
    Else
        PerfectCubeTest = "Flawed"
    End If

End Function

Or (Thanks to Ron)

Function PerfectCubeTest(x As Double)

    If CDec(x ^ (1 / 3)) = Int(CDec(x ^ (1 / 3))) Then
        PerfectCubeTest = "Perfect"
    Else
        PerfectCubeTest = "Flawed"
    End If


End Function

enter image description here

like image 100
Scott Craner Avatar answered Sep 20 '22 10:09

Scott Craner


@ScottCraner correctly explains why you were getting incorrect results, but there are a couple other things to point out here. First, I'm assuming that you are taking a Double as input because the range of acceptable numbers is higher. However, by your implied definition of a perfect cube only numbers with an integer cube root (i.e. it would exclude 3.375) need to be evaluated. I'd just test for this up front to allow an early exit.

The next issue you run into is that 1 / 3 can't be represented exactly by a Double. Since you're raising to the inverse power to get your cube root you're also compounding the floating point error. There's a really easy way to avoid this - take the cube root, cube it, and see if it matches the input. You get around the rest of the floating point errors by going back to your definition of a perfect cube as an integer value - just round the cube root to both the next higher and next lower integer before you re-cube it:

Public Function IsPerfectCube(test As Double) As Boolean
    'By your definition, no non-integer can be a perfect cube.
    Dim rounded As Double
    rounded = Fix(test)
    If rounded <> test Then Exit Function

    Dim cubeRoot As Double
    cubeRoot = rounded ^ (1 / 3)
    'Round both ways, then test the cube for equity.
    If Fix(cubeRoot) ^ 3 = rounded Then
        IsPerfectCube = True
    ElseIf (Fix(cubeRoot) + 1) ^ 3 = rounded Then
        IsPerfectCube = True
    End If
End Function

This returned the correct result up to 1E+27 (1 billion cubed) when I tested it. I stopped going higher at that point because the test was taking so long to run and by that point you're probably outside of the range that you would reasonably need it to be accurate.

like image 38
Comintern Avatar answered Sep 18 '22 10:09

Comintern