Skip to main content
deleted 4 characters in body
Source Link
G. Sliepen
  • 69.5k
  • 3
  • 75
  • 180

Use complex numbers

Since C99, C supports complex numbers, and has many mathematical functions that work on complex numbers. I would rewrite all your code to use that. For example, for a Newton fractal with \$p(z) = z^3 - 1\$:

static complex double next(complex double z)
{
    complex double denominator = 3 * z * z;
    complex double numerator = z * z * z - 1;
    return z - numerator / denominator;
}

This will make your code look much nicer. It may or may not be more performant, but that brings me to:

Use -ffast-math and -march=native

While in pure mathematics, multiplication of real and complex numbers is distributativeassociative, it's not for floating point numbers stored in computers. The reason is that after every operation, the calculated number is rounded back to the precision of a float or double. The order of operations therefore matters for the final result. Compilers won't reorder those operations by default. However, if you use the -ffast-math compiler flag (or /fp:fast for MSVC), then it will be able to reorder operations, even if it might change the precision of the result.

By default, compilers also compile binaries that are guaranteed to run on a wide range of processors; for example if you compile for the amd64 architecture, it will run on all amd64 machines, even old ones. If you have a newer CPU however, it might have some extra instructions that could have sped up your calculation, but would break backwards compatibility with older CPUs. If you use the -march=native compiler flag, then it will use newer instructions if possible.

Use complex numbers

Since C99, C supports complex numbers, and has many mathematical functions that work on complex numbers. I would rewrite all your code to use that. For example, for a Newton fractal with \$p(z) = z^3 - 1\$:

static complex double next(complex double z)
{
    complex double denominator = 3 * z * z;
    complex double numerator = z * z * z - 1;
    return z - numerator / denominator;
}

This will make your code look much nicer. It may or may not be more performant, but that brings me to:

Use -ffast-math and -march=native

While in pure mathematics, multiplication of real and complex numbers is distributative, it's not for floating point numbers stored in computers. The reason is that after every operation, the calculated number is rounded back to the precision of a float or double. The order of operations therefore matters for the final result. Compilers won't reorder those operations by default. However, if you use the -ffast-math compiler flag (or /fp:fast for MSVC), then it will be able to reorder operations, even if it might change the precision of the result.

By default, compilers also compile binaries that are guaranteed to run on a wide range of processors; for example if you compile for the amd64 architecture, it will run on all amd64 machines, even old ones. If you have a newer CPU however, it might have some extra instructions that could have sped up your calculation, but would break backwards compatibility with older CPUs. If you use the -march=native compiler flag, then it will use newer instructions if possible.

Use complex numbers

Since C99, C supports complex numbers, and has many mathematical functions that work on complex numbers. I would rewrite all your code to use that. For example, for a Newton fractal with \$p(z) = z^3 - 1\$:

static complex double next(complex double z)
{
    complex double denominator = 3 * z * z;
    complex double numerator = z * z * z - 1;
    return z - numerator / denominator;
}

This will make your code look much nicer. It may or may not be more performant, but that brings me to:

Use -ffast-math and -march=native

While in pure mathematics, multiplication of real and complex numbers is associative, it's not for floating point numbers stored in computers. The reason is that after every operation, the calculated number is rounded back to the precision of a float or double. The order of operations therefore matters for the final result. Compilers won't reorder those operations by default. However, if you use the -ffast-math compiler flag (or /fp:fast for MSVC), then it will be able to reorder operations, even if it might change the precision of the result.

By default, compilers also compile binaries that are guaranteed to run on a wide range of processors; for example if you compile for the amd64 architecture, it will run on all amd64 machines, even old ones. If you have a newer CPU however, it might have some extra instructions that could have sped up your calculation, but would break backwards compatibility with older CPUs. If you use the -march=native compiler flag, then it will use newer instructions if possible.

Source Link
G. Sliepen
  • 69.5k
  • 3
  • 75
  • 180

Use complex numbers

Since C99, C supports complex numbers, and has many mathematical functions that work on complex numbers. I would rewrite all your code to use that. For example, for a Newton fractal with \$p(z) = z^3 - 1\$:

static complex double next(complex double z)
{
    complex double denominator = 3 * z * z;
    complex double numerator = z * z * z - 1;
    return z - numerator / denominator;
}

This will make your code look much nicer. It may or may not be more performant, but that brings me to:

Use -ffast-math and -march=native

While in pure mathematics, multiplication of real and complex numbers is distributative, it's not for floating point numbers stored in computers. The reason is that after every operation, the calculated number is rounded back to the precision of a float or double. The order of operations therefore matters for the final result. Compilers won't reorder those operations by default. However, if you use the -ffast-math compiler flag (or /fp:fast for MSVC), then it will be able to reorder operations, even if it might change the precision of the result.

By default, compilers also compile binaries that are guaranteed to run on a wide range of processors; for example if you compile for the amd64 architecture, it will run on all amd64 machines, even old ones. If you have a newer CPU however, it might have some extra instructions that could have sped up your calculation, but would break backwards compatibility with older CPUs. If you use the -march=native compiler flag, then it will use newer instructions if possible.